18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci    Conexant cx24117/cx24132 - Dual DVBS/S2 Satellite demod/tuner driver
48c2ecf20Sopenharmony_ci
58c2ecf20Sopenharmony_ci    Copyright (C) 2013 Luis Alves <ljalvs@gmail.com>
68c2ecf20Sopenharmony_ci	July, 6th 2013
78c2ecf20Sopenharmony_ci	    First release based on cx24116 driver by:
88c2ecf20Sopenharmony_ci	    Steven Toth and Georg Acher, Darron Broad, Igor Liplianin
98c2ecf20Sopenharmony_ci	    Cards currently supported:
108c2ecf20Sopenharmony_ci		TBS6980 - Dual DVBS/S2 PCIe card
118c2ecf20Sopenharmony_ci		TBS6981 - Dual DVBS/S2 PCIe card
128c2ecf20Sopenharmony_ci
138c2ecf20Sopenharmony_ci*/
148c2ecf20Sopenharmony_ci
158c2ecf20Sopenharmony_ci#include <linux/slab.h>
168c2ecf20Sopenharmony_ci#include <linux/kernel.h>
178c2ecf20Sopenharmony_ci#include <linux/module.h>
188c2ecf20Sopenharmony_ci#include <linux/moduleparam.h>
198c2ecf20Sopenharmony_ci#include <linux/init.h>
208c2ecf20Sopenharmony_ci#include <linux/firmware.h>
218c2ecf20Sopenharmony_ci
228c2ecf20Sopenharmony_ci#include "tuner-i2c.h"
238c2ecf20Sopenharmony_ci#include <media/dvb_frontend.h>
248c2ecf20Sopenharmony_ci#include "cx24117.h"
258c2ecf20Sopenharmony_ci
268c2ecf20Sopenharmony_ci
278c2ecf20Sopenharmony_ci#define CX24117_DEFAULT_FIRMWARE "dvb-fe-cx24117.fw"
288c2ecf20Sopenharmony_ci#define CX24117_SEARCH_RANGE_KHZ 5000
298c2ecf20Sopenharmony_ci
308c2ecf20Sopenharmony_ci/* known registers */
318c2ecf20Sopenharmony_ci#define CX24117_REG_COMMAND      (0x00)      /* command buffer */
328c2ecf20Sopenharmony_ci#define CX24117_REG_EXECUTE      (0x1f)      /* execute command */
338c2ecf20Sopenharmony_ci
348c2ecf20Sopenharmony_ci#define CX24117_REG_FREQ3_0      (0x34)      /* frequency */
358c2ecf20Sopenharmony_ci#define CX24117_REG_FREQ2_0      (0x35)
368c2ecf20Sopenharmony_ci#define CX24117_REG_FREQ1_0      (0x36)
378c2ecf20Sopenharmony_ci#define CX24117_REG_STATE0       (0x39)
388c2ecf20Sopenharmony_ci#define CX24117_REG_SSTATUS0     (0x3a)      /* demod0 signal high / status */
398c2ecf20Sopenharmony_ci#define CX24117_REG_SIGNAL0      (0x3b)
408c2ecf20Sopenharmony_ci#define CX24117_REG_FREQ5_0      (0x3c)      /* +-freq */
418c2ecf20Sopenharmony_ci#define CX24117_REG_FREQ6_0      (0x3d)
428c2ecf20Sopenharmony_ci#define CX24117_REG_SRATE2_0     (0x3e)      /* +- 1000 * srate */
438c2ecf20Sopenharmony_ci#define CX24117_REG_SRATE1_0     (0x3f)
448c2ecf20Sopenharmony_ci#define CX24117_REG_QUALITY2_0   (0x40)
458c2ecf20Sopenharmony_ci#define CX24117_REG_QUALITY1_0   (0x41)
468c2ecf20Sopenharmony_ci
478c2ecf20Sopenharmony_ci#define CX24117_REG_BER4_0       (0x47)
488c2ecf20Sopenharmony_ci#define CX24117_REG_BER3_0       (0x48)
498c2ecf20Sopenharmony_ci#define CX24117_REG_BER2_0       (0x49)
508c2ecf20Sopenharmony_ci#define CX24117_REG_BER1_0       (0x4a)
518c2ecf20Sopenharmony_ci#define CX24117_REG_DVBS_UCB2_0  (0x4b)
528c2ecf20Sopenharmony_ci#define CX24117_REG_DVBS_UCB1_0  (0x4c)
538c2ecf20Sopenharmony_ci#define CX24117_REG_DVBS2_UCB2_0 (0x50)
548c2ecf20Sopenharmony_ci#define CX24117_REG_DVBS2_UCB1_0 (0x51)
558c2ecf20Sopenharmony_ci#define CX24117_REG_QSTATUS0     (0x93)
568c2ecf20Sopenharmony_ci#define CX24117_REG_CLKDIV0      (0xe6)
578c2ecf20Sopenharmony_ci#define CX24117_REG_RATEDIV0     (0xf0)
588c2ecf20Sopenharmony_ci
598c2ecf20Sopenharmony_ci
608c2ecf20Sopenharmony_ci#define CX24117_REG_FREQ3_1      (0x55)      /* frequency */
618c2ecf20Sopenharmony_ci#define CX24117_REG_FREQ2_1      (0x56)
628c2ecf20Sopenharmony_ci#define CX24117_REG_FREQ1_1      (0x57)
638c2ecf20Sopenharmony_ci#define CX24117_REG_STATE1       (0x5a)
648c2ecf20Sopenharmony_ci#define CX24117_REG_SSTATUS1     (0x5b)      /* demod1 signal high / status */
658c2ecf20Sopenharmony_ci#define CX24117_REG_SIGNAL1      (0x5c)
668c2ecf20Sopenharmony_ci#define CX24117_REG_FREQ5_1      (0x5d)      /* +- freq */
678c2ecf20Sopenharmony_ci#define CX24117_REG_FREQ4_1      (0x5e)
688c2ecf20Sopenharmony_ci#define CX24117_REG_SRATE2_1     (0x5f)
698c2ecf20Sopenharmony_ci#define CX24117_REG_SRATE1_1     (0x60)
708c2ecf20Sopenharmony_ci#define CX24117_REG_QUALITY2_1   (0x61)
718c2ecf20Sopenharmony_ci#define CX24117_REG_QUALITY1_1   (0x62)
728c2ecf20Sopenharmony_ci#define CX24117_REG_BER4_1       (0x68)
738c2ecf20Sopenharmony_ci#define CX24117_REG_BER3_1       (0x69)
748c2ecf20Sopenharmony_ci#define CX24117_REG_BER2_1       (0x6a)
758c2ecf20Sopenharmony_ci#define CX24117_REG_BER1_1       (0x6b)
768c2ecf20Sopenharmony_ci#define CX24117_REG_DVBS_UCB2_1  (0x6c)
778c2ecf20Sopenharmony_ci#define CX24117_REG_DVBS_UCB1_1  (0x6d)
788c2ecf20Sopenharmony_ci#define CX24117_REG_DVBS2_UCB2_1 (0x71)
798c2ecf20Sopenharmony_ci#define CX24117_REG_DVBS2_UCB1_1 (0x72)
808c2ecf20Sopenharmony_ci#define CX24117_REG_QSTATUS1     (0x9f)
818c2ecf20Sopenharmony_ci#define CX24117_REG_CLKDIV1      (0xe7)
828c2ecf20Sopenharmony_ci#define CX24117_REG_RATEDIV1     (0xf1)
838c2ecf20Sopenharmony_ci
848c2ecf20Sopenharmony_ci
858c2ecf20Sopenharmony_ci/* arg buffer size */
868c2ecf20Sopenharmony_ci#define CX24117_ARGLEN       (0x1e)
878c2ecf20Sopenharmony_ci
888c2ecf20Sopenharmony_ci/* rolloff */
898c2ecf20Sopenharmony_ci#define CX24117_ROLLOFF_020  (0x00)
908c2ecf20Sopenharmony_ci#define CX24117_ROLLOFF_025  (0x01)
918c2ecf20Sopenharmony_ci#define CX24117_ROLLOFF_035  (0x02)
928c2ecf20Sopenharmony_ci
938c2ecf20Sopenharmony_ci/* pilot bit */
948c2ecf20Sopenharmony_ci#define CX24117_PILOT_OFF    (0x00)
958c2ecf20Sopenharmony_ci#define CX24117_PILOT_ON     (0x40)
968c2ecf20Sopenharmony_ci#define CX24117_PILOT_AUTO   (0x80)
978c2ecf20Sopenharmony_ci
988c2ecf20Sopenharmony_ci/* signal status */
998c2ecf20Sopenharmony_ci#define CX24117_HAS_SIGNAL   (0x01)
1008c2ecf20Sopenharmony_ci#define CX24117_HAS_CARRIER  (0x02)
1018c2ecf20Sopenharmony_ci#define CX24117_HAS_VITERBI  (0x04)
1028c2ecf20Sopenharmony_ci#define CX24117_HAS_SYNCLOCK (0x08)
1038c2ecf20Sopenharmony_ci#define CX24117_STATUS_MASK  (0x0f)
1048c2ecf20Sopenharmony_ci#define CX24117_SIGNAL_MASK  (0xc0)
1058c2ecf20Sopenharmony_ci
1068c2ecf20Sopenharmony_ci
1078c2ecf20Sopenharmony_ci/* arg offset for DiSEqC */
1088c2ecf20Sopenharmony_ci#define CX24117_DISEQC_DEMOD  (1)
1098c2ecf20Sopenharmony_ci#define CX24117_DISEQC_BURST  (2)
1108c2ecf20Sopenharmony_ci#define CX24117_DISEQC_ARG3_2 (3)   /* unknown value=2 */
1118c2ecf20Sopenharmony_ci#define CX24117_DISEQC_ARG4_0 (4)   /* unknown value=0 */
1128c2ecf20Sopenharmony_ci#define CX24117_DISEQC_ARG5_0 (5)   /* unknown value=0 */
1138c2ecf20Sopenharmony_ci#define CX24117_DISEQC_MSGLEN (6)
1148c2ecf20Sopenharmony_ci#define CX24117_DISEQC_MSGOFS (7)
1158c2ecf20Sopenharmony_ci
1168c2ecf20Sopenharmony_ci/* DiSEqC burst */
1178c2ecf20Sopenharmony_ci#define CX24117_DISEQC_MINI_A (0)
1188c2ecf20Sopenharmony_ci#define CX24117_DISEQC_MINI_B (1)
1198c2ecf20Sopenharmony_ci
1208c2ecf20Sopenharmony_ci
1218c2ecf20Sopenharmony_ci#define CX24117_PNE	(0) /* 0 disabled / 2 enabled */
1228c2ecf20Sopenharmony_ci#define CX24117_OCC	(1) /* 0 disabled / 1 enabled */
1238c2ecf20Sopenharmony_ci
1248c2ecf20Sopenharmony_ci
1258c2ecf20Sopenharmony_cienum cmds {
1268c2ecf20Sopenharmony_ci	CMD_SET_VCOFREQ    = 0x10,
1278c2ecf20Sopenharmony_ci	CMD_TUNEREQUEST    = 0x11,
1288c2ecf20Sopenharmony_ci	CMD_GLOBAL_MPEGCFG = 0x13,
1298c2ecf20Sopenharmony_ci	CMD_MPEGCFG        = 0x14,
1308c2ecf20Sopenharmony_ci	CMD_TUNERINIT      = 0x15,
1318c2ecf20Sopenharmony_ci	CMD_GET_SRATE      = 0x18,
1328c2ecf20Sopenharmony_ci	CMD_SET_GOLDCODE   = 0x19,
1338c2ecf20Sopenharmony_ci	CMD_GET_AGCACC     = 0x1a,
1348c2ecf20Sopenharmony_ci	CMD_DEMODINIT      = 0x1b,
1358c2ecf20Sopenharmony_ci	CMD_GETCTLACC      = 0x1c,
1368c2ecf20Sopenharmony_ci
1378c2ecf20Sopenharmony_ci	CMD_LNBCONFIG      = 0x20,
1388c2ecf20Sopenharmony_ci	CMD_LNBSEND        = 0x21,
1398c2ecf20Sopenharmony_ci	CMD_LNBDCLEVEL     = 0x22,
1408c2ecf20Sopenharmony_ci	CMD_LNBPCBCONFIG   = 0x23,
1418c2ecf20Sopenharmony_ci	CMD_LNBSENDTONEBST = 0x24,
1428c2ecf20Sopenharmony_ci	CMD_LNBUPDREPLY    = 0x25,
1438c2ecf20Sopenharmony_ci
1448c2ecf20Sopenharmony_ci	CMD_SET_GPIOMODE   = 0x30,
1458c2ecf20Sopenharmony_ci	CMD_SET_GPIOEN     = 0x31,
1468c2ecf20Sopenharmony_ci	CMD_SET_GPIODIR    = 0x32,
1478c2ecf20Sopenharmony_ci	CMD_SET_GPIOOUT    = 0x33,
1488c2ecf20Sopenharmony_ci	CMD_ENABLERSCORR   = 0x34,
1498c2ecf20Sopenharmony_ci	CMD_FWVERSION      = 0x35,
1508c2ecf20Sopenharmony_ci	CMD_SET_SLEEPMODE  = 0x36,
1518c2ecf20Sopenharmony_ci	CMD_BERCTRL        = 0x3c,
1528c2ecf20Sopenharmony_ci	CMD_EVENTCTRL      = 0x3d,
1538c2ecf20Sopenharmony_ci};
1548c2ecf20Sopenharmony_ci
1558c2ecf20Sopenharmony_cistatic LIST_HEAD(hybrid_tuner_instance_list);
1568c2ecf20Sopenharmony_cistatic DEFINE_MUTEX(cx24117_list_mutex);
1578c2ecf20Sopenharmony_ci
1588c2ecf20Sopenharmony_ci/* The Demod/Tuner can't easily provide these, we cache them */
1598c2ecf20Sopenharmony_cistruct cx24117_tuning {
1608c2ecf20Sopenharmony_ci	u32 frequency;
1618c2ecf20Sopenharmony_ci	u32 symbol_rate;
1628c2ecf20Sopenharmony_ci	enum fe_spectral_inversion inversion;
1638c2ecf20Sopenharmony_ci	enum fe_code_rate fec;
1648c2ecf20Sopenharmony_ci
1658c2ecf20Sopenharmony_ci	enum fe_delivery_system delsys;
1668c2ecf20Sopenharmony_ci	enum fe_modulation modulation;
1678c2ecf20Sopenharmony_ci	enum fe_pilot pilot;
1688c2ecf20Sopenharmony_ci	enum fe_rolloff rolloff;
1698c2ecf20Sopenharmony_ci
1708c2ecf20Sopenharmony_ci	/* Demod values */
1718c2ecf20Sopenharmony_ci	u8 fec_val;
1728c2ecf20Sopenharmony_ci	u8 fec_mask;
1738c2ecf20Sopenharmony_ci	u8 inversion_val;
1748c2ecf20Sopenharmony_ci	u8 pilot_val;
1758c2ecf20Sopenharmony_ci	u8 rolloff_val;
1768c2ecf20Sopenharmony_ci};
1778c2ecf20Sopenharmony_ci
1788c2ecf20Sopenharmony_ci/* Basic commands that are sent to the firmware */
1798c2ecf20Sopenharmony_cistruct cx24117_cmd {
1808c2ecf20Sopenharmony_ci	u8 len;
1818c2ecf20Sopenharmony_ci	u8 args[CX24117_ARGLEN];
1828c2ecf20Sopenharmony_ci};
1838c2ecf20Sopenharmony_ci
1848c2ecf20Sopenharmony_ci/* common to both fe's */
1858c2ecf20Sopenharmony_cistruct cx24117_priv {
1868c2ecf20Sopenharmony_ci	u8 demod_address;
1878c2ecf20Sopenharmony_ci	struct i2c_adapter *i2c;
1888c2ecf20Sopenharmony_ci	u8 skip_fw_load;
1898c2ecf20Sopenharmony_ci	struct mutex fe_lock;
1908c2ecf20Sopenharmony_ci
1918c2ecf20Sopenharmony_ci	/* Used for sharing this struct between demods */
1928c2ecf20Sopenharmony_ci	struct tuner_i2c_props i2c_props;
1938c2ecf20Sopenharmony_ci	struct list_head hybrid_tuner_instance_list;
1948c2ecf20Sopenharmony_ci};
1958c2ecf20Sopenharmony_ci
1968c2ecf20Sopenharmony_ci/* one per each fe */
1978c2ecf20Sopenharmony_cistruct cx24117_state {
1988c2ecf20Sopenharmony_ci	struct cx24117_priv *priv;
1998c2ecf20Sopenharmony_ci	struct dvb_frontend frontend;
2008c2ecf20Sopenharmony_ci
2018c2ecf20Sopenharmony_ci	struct cx24117_tuning dcur;
2028c2ecf20Sopenharmony_ci	struct cx24117_tuning dnxt;
2038c2ecf20Sopenharmony_ci	struct cx24117_cmd dsec_cmd;
2048c2ecf20Sopenharmony_ci
2058c2ecf20Sopenharmony_ci	int demod;
2068c2ecf20Sopenharmony_ci};
2078c2ecf20Sopenharmony_ci
2088c2ecf20Sopenharmony_ci/* modfec (modulation and FEC) lookup table */
2098c2ecf20Sopenharmony_ci/* Check cx24116.c for a detailed description of each field */
2108c2ecf20Sopenharmony_cistatic struct cx24117_modfec {
2118c2ecf20Sopenharmony_ci	enum fe_delivery_system delivery_system;
2128c2ecf20Sopenharmony_ci	enum fe_modulation modulation;
2138c2ecf20Sopenharmony_ci	enum fe_code_rate fec;
2148c2ecf20Sopenharmony_ci	u8 mask;	/* In DVBS mode this is used to autodetect */
2158c2ecf20Sopenharmony_ci	u8 val;		/* Passed to the firmware to indicate mode selection */
2168c2ecf20Sopenharmony_ci} cx24117_modfec_modes[] = {
2178c2ecf20Sopenharmony_ci	/* QPSK. For unknown rates we set hardware to auto detect 0xfe 0x30 */
2188c2ecf20Sopenharmony_ci
2198c2ecf20Sopenharmony_ci	/*mod   fec       mask  val */
2208c2ecf20Sopenharmony_ci	{ SYS_DVBS, QPSK, FEC_NONE, 0xfe, 0x30 },
2218c2ecf20Sopenharmony_ci	{ SYS_DVBS, QPSK, FEC_1_2,  0x02, 0x2e }, /* 00000010 00101110 */
2228c2ecf20Sopenharmony_ci	{ SYS_DVBS, QPSK, FEC_2_3,  0x04, 0x2f }, /* 00000100 00101111 */
2238c2ecf20Sopenharmony_ci	{ SYS_DVBS, QPSK, FEC_3_4,  0x08, 0x30 }, /* 00001000 00110000 */
2248c2ecf20Sopenharmony_ci	{ SYS_DVBS, QPSK, FEC_4_5,  0xfe, 0x30 }, /* 000?0000 ?        */
2258c2ecf20Sopenharmony_ci	{ SYS_DVBS, QPSK, FEC_5_6,  0x20, 0x31 }, /* 00100000 00110001 */
2268c2ecf20Sopenharmony_ci	{ SYS_DVBS, QPSK, FEC_6_7,  0xfe, 0x30 }, /* 0?000000 ?        */
2278c2ecf20Sopenharmony_ci	{ SYS_DVBS, QPSK, FEC_7_8,  0x80, 0x32 }, /* 10000000 00110010 */
2288c2ecf20Sopenharmony_ci	{ SYS_DVBS, QPSK, FEC_8_9,  0xfe, 0x30 }, /* 0000000? ?        */
2298c2ecf20Sopenharmony_ci	{ SYS_DVBS, QPSK, FEC_AUTO, 0xfe, 0x30 },
2308c2ecf20Sopenharmony_ci	/* NBC-QPSK */
2318c2ecf20Sopenharmony_ci	{ SYS_DVBS2, QPSK, FEC_NONE, 0x00, 0x00 },
2328c2ecf20Sopenharmony_ci	{ SYS_DVBS2, QPSK, FEC_1_2,  0x00, 0x04 },
2338c2ecf20Sopenharmony_ci	{ SYS_DVBS2, QPSK, FEC_3_5,  0x00, 0x05 },
2348c2ecf20Sopenharmony_ci	{ SYS_DVBS2, QPSK, FEC_2_3,  0x00, 0x06 },
2358c2ecf20Sopenharmony_ci	{ SYS_DVBS2, QPSK, FEC_3_4,  0x00, 0x07 },
2368c2ecf20Sopenharmony_ci	{ SYS_DVBS2, QPSK, FEC_4_5,  0x00, 0x08 },
2378c2ecf20Sopenharmony_ci	{ SYS_DVBS2, QPSK, FEC_5_6,  0x00, 0x09 },
2388c2ecf20Sopenharmony_ci	{ SYS_DVBS2, QPSK, FEC_8_9,  0x00, 0x0a },
2398c2ecf20Sopenharmony_ci	{ SYS_DVBS2, QPSK, FEC_9_10, 0x00, 0x0b },
2408c2ecf20Sopenharmony_ci	{ SYS_DVBS2, QPSK, FEC_AUTO, 0x00, 0x00 },
2418c2ecf20Sopenharmony_ci	/* 8PSK */
2428c2ecf20Sopenharmony_ci	{ SYS_DVBS2, PSK_8, FEC_NONE, 0x00, 0x00 },
2438c2ecf20Sopenharmony_ci	{ SYS_DVBS2, PSK_8, FEC_3_5,  0x00, 0x0c },
2448c2ecf20Sopenharmony_ci	{ SYS_DVBS2, PSK_8, FEC_2_3,  0x00, 0x0d },
2458c2ecf20Sopenharmony_ci	{ SYS_DVBS2, PSK_8, FEC_3_4,  0x00, 0x0e },
2468c2ecf20Sopenharmony_ci	{ SYS_DVBS2, PSK_8, FEC_5_6,  0x00, 0x0f },
2478c2ecf20Sopenharmony_ci	{ SYS_DVBS2, PSK_8, FEC_8_9,  0x00, 0x10 },
2488c2ecf20Sopenharmony_ci	{ SYS_DVBS2, PSK_8, FEC_9_10, 0x00, 0x11 },
2498c2ecf20Sopenharmony_ci	{ SYS_DVBS2, PSK_8, FEC_AUTO, 0x00, 0x00 },
2508c2ecf20Sopenharmony_ci	/*
2518c2ecf20Sopenharmony_ci	 * 'val' can be found in the FECSTATUS register when tuning.
2528c2ecf20Sopenharmony_ci	 * FECSTATUS will give the actual FEC in use if tuning was successful.
2538c2ecf20Sopenharmony_ci	 */
2548c2ecf20Sopenharmony_ci};
2558c2ecf20Sopenharmony_ci
2568c2ecf20Sopenharmony_ci
2578c2ecf20Sopenharmony_cistatic int cx24117_writereg(struct cx24117_state *state, u8 reg, u8 data)
2588c2ecf20Sopenharmony_ci{
2598c2ecf20Sopenharmony_ci	u8 buf[] = { reg, data };
2608c2ecf20Sopenharmony_ci	struct i2c_msg msg = { .addr = state->priv->demod_address,
2618c2ecf20Sopenharmony_ci		.flags = 0, .buf = buf, .len = 2 };
2628c2ecf20Sopenharmony_ci	int ret;
2638c2ecf20Sopenharmony_ci
2648c2ecf20Sopenharmony_ci	dev_dbg(&state->priv->i2c->dev,
2658c2ecf20Sopenharmony_ci			"%s() demod%d i2c wr @0x%02x=0x%02x\n",
2668c2ecf20Sopenharmony_ci			__func__, state->demod, reg, data);
2678c2ecf20Sopenharmony_ci
2688c2ecf20Sopenharmony_ci	ret = i2c_transfer(state->priv->i2c, &msg, 1);
2698c2ecf20Sopenharmony_ci	if (ret < 0) {
2708c2ecf20Sopenharmony_ci		dev_warn(&state->priv->i2c->dev,
2718c2ecf20Sopenharmony_ci			"%s: demod%d i2c wr err(%i) @0x%02x=0x%02x\n",
2728c2ecf20Sopenharmony_ci			KBUILD_MODNAME, state->demod, ret, reg, data);
2738c2ecf20Sopenharmony_ci		return ret;
2748c2ecf20Sopenharmony_ci	}
2758c2ecf20Sopenharmony_ci	return 0;
2768c2ecf20Sopenharmony_ci}
2778c2ecf20Sopenharmony_ci
2788c2ecf20Sopenharmony_cistatic int cx24117_writecmd(struct cx24117_state *state,
2798c2ecf20Sopenharmony_ci	struct cx24117_cmd *cmd)
2808c2ecf20Sopenharmony_ci{
2818c2ecf20Sopenharmony_ci	struct i2c_msg msg;
2828c2ecf20Sopenharmony_ci	u8 buf[CX24117_ARGLEN+1];
2838c2ecf20Sopenharmony_ci	int ret;
2848c2ecf20Sopenharmony_ci
2858c2ecf20Sopenharmony_ci	dev_dbg(&state->priv->i2c->dev,
2868c2ecf20Sopenharmony_ci			"%s() demod%d i2c wr cmd len=%d\n",
2878c2ecf20Sopenharmony_ci			__func__, state->demod, cmd->len);
2888c2ecf20Sopenharmony_ci
2898c2ecf20Sopenharmony_ci	buf[0] = CX24117_REG_COMMAND;
2908c2ecf20Sopenharmony_ci	memcpy(&buf[1], cmd->args, cmd->len);
2918c2ecf20Sopenharmony_ci
2928c2ecf20Sopenharmony_ci	msg.addr = state->priv->demod_address;
2938c2ecf20Sopenharmony_ci	msg.flags = 0;
2948c2ecf20Sopenharmony_ci	msg.len = cmd->len+1;
2958c2ecf20Sopenharmony_ci	msg.buf = buf;
2968c2ecf20Sopenharmony_ci	ret = i2c_transfer(state->priv->i2c, &msg, 1);
2978c2ecf20Sopenharmony_ci	if (ret < 0) {
2988c2ecf20Sopenharmony_ci		dev_warn(&state->priv->i2c->dev,
2998c2ecf20Sopenharmony_ci			"%s: demod%d i2c wr cmd err(%i) len=%d\n",
3008c2ecf20Sopenharmony_ci			KBUILD_MODNAME, state->demod, ret, cmd->len);
3018c2ecf20Sopenharmony_ci		return ret;
3028c2ecf20Sopenharmony_ci	}
3038c2ecf20Sopenharmony_ci	return 0;
3048c2ecf20Sopenharmony_ci}
3058c2ecf20Sopenharmony_ci
3068c2ecf20Sopenharmony_cistatic int cx24117_readreg(struct cx24117_state *state, u8 reg)
3078c2ecf20Sopenharmony_ci{
3088c2ecf20Sopenharmony_ci	int ret;
3098c2ecf20Sopenharmony_ci	u8 recv = 0;
3108c2ecf20Sopenharmony_ci	struct i2c_msg msg[] = {
3118c2ecf20Sopenharmony_ci		{ .addr = state->priv->demod_address, .flags = 0,
3128c2ecf20Sopenharmony_ci			.buf = &reg, .len = 1 },
3138c2ecf20Sopenharmony_ci		{ .addr = state->priv->demod_address, .flags = I2C_M_RD,
3148c2ecf20Sopenharmony_ci			.buf = &recv, .len = 1 }
3158c2ecf20Sopenharmony_ci	};
3168c2ecf20Sopenharmony_ci
3178c2ecf20Sopenharmony_ci	ret = i2c_transfer(state->priv->i2c, msg, 2);
3188c2ecf20Sopenharmony_ci	if (ret < 0) {
3198c2ecf20Sopenharmony_ci		dev_warn(&state->priv->i2c->dev,
3208c2ecf20Sopenharmony_ci			"%s: demod%d i2c rd err(%d) @0x%x\n",
3218c2ecf20Sopenharmony_ci			KBUILD_MODNAME, state->demod, ret, reg);
3228c2ecf20Sopenharmony_ci		return ret;
3238c2ecf20Sopenharmony_ci	}
3248c2ecf20Sopenharmony_ci
3258c2ecf20Sopenharmony_ci	dev_dbg(&state->priv->i2c->dev, "%s() demod%d i2c rd @0x%02x=0x%02x\n",
3268c2ecf20Sopenharmony_ci		__func__, state->demod, reg, recv);
3278c2ecf20Sopenharmony_ci
3288c2ecf20Sopenharmony_ci	return recv;
3298c2ecf20Sopenharmony_ci}
3308c2ecf20Sopenharmony_ci
3318c2ecf20Sopenharmony_cistatic int cx24117_readregN(struct cx24117_state *state,
3328c2ecf20Sopenharmony_ci	u8 reg, u8 *buf, int len)
3338c2ecf20Sopenharmony_ci{
3348c2ecf20Sopenharmony_ci	int ret;
3358c2ecf20Sopenharmony_ci	struct i2c_msg msg[] = {
3368c2ecf20Sopenharmony_ci		{ .addr = state->priv->demod_address, .flags = 0,
3378c2ecf20Sopenharmony_ci			.buf = &reg, .len = 1 },
3388c2ecf20Sopenharmony_ci		{ .addr = state->priv->demod_address, .flags = I2C_M_RD,
3398c2ecf20Sopenharmony_ci			.buf = buf, .len = len }
3408c2ecf20Sopenharmony_ci	};
3418c2ecf20Sopenharmony_ci
3428c2ecf20Sopenharmony_ci	ret = i2c_transfer(state->priv->i2c, msg, 2);
3438c2ecf20Sopenharmony_ci	if (ret < 0) {
3448c2ecf20Sopenharmony_ci		dev_warn(&state->priv->i2c->dev,
3458c2ecf20Sopenharmony_ci			"%s: demod%d i2c rd err(%d) @0x%x\n",
3468c2ecf20Sopenharmony_ci			KBUILD_MODNAME, state->demod, ret, reg);
3478c2ecf20Sopenharmony_ci		return ret;
3488c2ecf20Sopenharmony_ci	}
3498c2ecf20Sopenharmony_ci	return 0;
3508c2ecf20Sopenharmony_ci}
3518c2ecf20Sopenharmony_ci
3528c2ecf20Sopenharmony_cistatic int cx24117_set_inversion(struct cx24117_state *state,
3538c2ecf20Sopenharmony_ci	enum fe_spectral_inversion inversion)
3548c2ecf20Sopenharmony_ci{
3558c2ecf20Sopenharmony_ci	dev_dbg(&state->priv->i2c->dev, "%s(%d) demod%d\n",
3568c2ecf20Sopenharmony_ci		__func__, inversion, state->demod);
3578c2ecf20Sopenharmony_ci
3588c2ecf20Sopenharmony_ci	switch (inversion) {
3598c2ecf20Sopenharmony_ci	case INVERSION_OFF:
3608c2ecf20Sopenharmony_ci		state->dnxt.inversion_val = 0x00;
3618c2ecf20Sopenharmony_ci		break;
3628c2ecf20Sopenharmony_ci	case INVERSION_ON:
3638c2ecf20Sopenharmony_ci		state->dnxt.inversion_val = 0x04;
3648c2ecf20Sopenharmony_ci		break;
3658c2ecf20Sopenharmony_ci	case INVERSION_AUTO:
3668c2ecf20Sopenharmony_ci		state->dnxt.inversion_val = 0x0C;
3678c2ecf20Sopenharmony_ci		break;
3688c2ecf20Sopenharmony_ci	default:
3698c2ecf20Sopenharmony_ci		return -EINVAL;
3708c2ecf20Sopenharmony_ci	}
3718c2ecf20Sopenharmony_ci
3728c2ecf20Sopenharmony_ci	state->dnxt.inversion = inversion;
3738c2ecf20Sopenharmony_ci
3748c2ecf20Sopenharmony_ci	return 0;
3758c2ecf20Sopenharmony_ci}
3768c2ecf20Sopenharmony_ci
3778c2ecf20Sopenharmony_cistatic int cx24117_lookup_fecmod(struct cx24117_state *state,
3788c2ecf20Sopenharmony_ci	enum fe_delivery_system d, enum fe_modulation m, enum fe_code_rate f)
3798c2ecf20Sopenharmony_ci{
3808c2ecf20Sopenharmony_ci	int i, ret = -EINVAL;
3818c2ecf20Sopenharmony_ci
3828c2ecf20Sopenharmony_ci	dev_dbg(&state->priv->i2c->dev,
3838c2ecf20Sopenharmony_ci		"%s(demod(0x%02x,0x%02x) demod%d\n",
3848c2ecf20Sopenharmony_ci		__func__, m, f, state->demod);
3858c2ecf20Sopenharmony_ci
3868c2ecf20Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(cx24117_modfec_modes); i++) {
3878c2ecf20Sopenharmony_ci		if ((d == cx24117_modfec_modes[i].delivery_system) &&
3888c2ecf20Sopenharmony_ci			(m == cx24117_modfec_modes[i].modulation) &&
3898c2ecf20Sopenharmony_ci			(f == cx24117_modfec_modes[i].fec)) {
3908c2ecf20Sopenharmony_ci				ret = i;
3918c2ecf20Sopenharmony_ci				break;
3928c2ecf20Sopenharmony_ci			}
3938c2ecf20Sopenharmony_ci	}
3948c2ecf20Sopenharmony_ci
3958c2ecf20Sopenharmony_ci	return ret;
3968c2ecf20Sopenharmony_ci}
3978c2ecf20Sopenharmony_ci
3988c2ecf20Sopenharmony_cistatic int cx24117_set_fec(struct cx24117_state *state,
3998c2ecf20Sopenharmony_ci			   enum fe_delivery_system delsys,
4008c2ecf20Sopenharmony_ci			   enum fe_modulation mod,
4018c2ecf20Sopenharmony_ci			   enum fe_code_rate fec)
4028c2ecf20Sopenharmony_ci{
4038c2ecf20Sopenharmony_ci	int ret;
4048c2ecf20Sopenharmony_ci
4058c2ecf20Sopenharmony_ci	dev_dbg(&state->priv->i2c->dev,
4068c2ecf20Sopenharmony_ci		"%s(0x%02x,0x%02x) demod%d\n",
4078c2ecf20Sopenharmony_ci		__func__, mod, fec, state->demod);
4088c2ecf20Sopenharmony_ci
4098c2ecf20Sopenharmony_ci	ret = cx24117_lookup_fecmod(state, delsys, mod, fec);
4108c2ecf20Sopenharmony_ci	if (ret < 0)
4118c2ecf20Sopenharmony_ci		return ret;
4128c2ecf20Sopenharmony_ci
4138c2ecf20Sopenharmony_ci	state->dnxt.fec = fec;
4148c2ecf20Sopenharmony_ci	state->dnxt.fec_val = cx24117_modfec_modes[ret].val;
4158c2ecf20Sopenharmony_ci	state->dnxt.fec_mask = cx24117_modfec_modes[ret].mask;
4168c2ecf20Sopenharmony_ci	dev_dbg(&state->priv->i2c->dev,
4178c2ecf20Sopenharmony_ci		"%s() demod%d mask/val = 0x%02x/0x%02x\n", __func__,
4188c2ecf20Sopenharmony_ci		state->demod, state->dnxt.fec_mask, state->dnxt.fec_val);
4198c2ecf20Sopenharmony_ci
4208c2ecf20Sopenharmony_ci	return 0;
4218c2ecf20Sopenharmony_ci}
4228c2ecf20Sopenharmony_ci
4238c2ecf20Sopenharmony_cistatic int cx24117_set_symbolrate(struct cx24117_state *state, u32 rate)
4248c2ecf20Sopenharmony_ci{
4258c2ecf20Sopenharmony_ci	dev_dbg(&state->priv->i2c->dev, "%s(%d) demod%d\n",
4268c2ecf20Sopenharmony_ci		__func__, rate, state->demod);
4278c2ecf20Sopenharmony_ci
4288c2ecf20Sopenharmony_ci	state->dnxt.symbol_rate = rate;
4298c2ecf20Sopenharmony_ci
4308c2ecf20Sopenharmony_ci	dev_dbg(&state->priv->i2c->dev,
4318c2ecf20Sopenharmony_ci		"%s() demod%d symbol_rate = %d\n",
4328c2ecf20Sopenharmony_ci		__func__, state->demod, rate);
4338c2ecf20Sopenharmony_ci
4348c2ecf20Sopenharmony_ci	return 0;
4358c2ecf20Sopenharmony_ci}
4368c2ecf20Sopenharmony_ci
4378c2ecf20Sopenharmony_cistatic int cx24117_load_firmware(struct dvb_frontend *fe,
4388c2ecf20Sopenharmony_ci	const struct firmware *fw);
4398c2ecf20Sopenharmony_ci
4408c2ecf20Sopenharmony_cistatic int cx24117_firmware_ondemand(struct dvb_frontend *fe)
4418c2ecf20Sopenharmony_ci{
4428c2ecf20Sopenharmony_ci	struct cx24117_state *state = fe->demodulator_priv;
4438c2ecf20Sopenharmony_ci	const struct firmware *fw;
4448c2ecf20Sopenharmony_ci	int ret = 0;
4458c2ecf20Sopenharmony_ci
4468c2ecf20Sopenharmony_ci	dev_dbg(&state->priv->i2c->dev, "%s() demod%d skip_fw_load=%d\n",
4478c2ecf20Sopenharmony_ci		__func__, state->demod, state->priv->skip_fw_load);
4488c2ecf20Sopenharmony_ci
4498c2ecf20Sopenharmony_ci	if (state->priv->skip_fw_load)
4508c2ecf20Sopenharmony_ci		return 0;
4518c2ecf20Sopenharmony_ci
4528c2ecf20Sopenharmony_ci	/* check if firmware is already running */
4538c2ecf20Sopenharmony_ci	if (cx24117_readreg(state, 0xeb) != 0xa) {
4548c2ecf20Sopenharmony_ci		/* Load firmware */
4558c2ecf20Sopenharmony_ci		/* request the firmware, this will block until loaded */
4568c2ecf20Sopenharmony_ci		dev_dbg(&state->priv->i2c->dev,
4578c2ecf20Sopenharmony_ci			"%s: Waiting for firmware upload (%s)...\n",
4588c2ecf20Sopenharmony_ci			__func__, CX24117_DEFAULT_FIRMWARE);
4598c2ecf20Sopenharmony_ci		ret = request_firmware(&fw, CX24117_DEFAULT_FIRMWARE,
4608c2ecf20Sopenharmony_ci			state->priv->i2c->dev.parent);
4618c2ecf20Sopenharmony_ci		dev_dbg(&state->priv->i2c->dev,
4628c2ecf20Sopenharmony_ci			"%s: Waiting for firmware upload(2)...\n", __func__);
4638c2ecf20Sopenharmony_ci		if (ret) {
4648c2ecf20Sopenharmony_ci			dev_err(&state->priv->i2c->dev,
4658c2ecf20Sopenharmony_ci				"%s: No firmware uploaded (timeout or file not found?)\n",
4668c2ecf20Sopenharmony_ci__func__);
4678c2ecf20Sopenharmony_ci			return ret;
4688c2ecf20Sopenharmony_ci		}
4698c2ecf20Sopenharmony_ci
4708c2ecf20Sopenharmony_ci		/* Make sure we don't recurse back through here
4718c2ecf20Sopenharmony_ci		 * during loading */
4728c2ecf20Sopenharmony_ci		state->priv->skip_fw_load = 1;
4738c2ecf20Sopenharmony_ci
4748c2ecf20Sopenharmony_ci		ret = cx24117_load_firmware(fe, fw);
4758c2ecf20Sopenharmony_ci		if (ret)
4768c2ecf20Sopenharmony_ci			dev_err(&state->priv->i2c->dev,
4778c2ecf20Sopenharmony_ci				"%s: Writing firmware failed\n", __func__);
4788c2ecf20Sopenharmony_ci		release_firmware(fw);
4798c2ecf20Sopenharmony_ci
4808c2ecf20Sopenharmony_ci		dev_info(&state->priv->i2c->dev,
4818c2ecf20Sopenharmony_ci			"%s: Firmware upload %s\n", __func__,
4828c2ecf20Sopenharmony_ci			ret == 0 ? "complete" : "failed");
4838c2ecf20Sopenharmony_ci
4848c2ecf20Sopenharmony_ci		/* Ensure firmware is always loaded if required */
4858c2ecf20Sopenharmony_ci		state->priv->skip_fw_load = 0;
4868c2ecf20Sopenharmony_ci	}
4878c2ecf20Sopenharmony_ci
4888c2ecf20Sopenharmony_ci	return ret;
4898c2ecf20Sopenharmony_ci}
4908c2ecf20Sopenharmony_ci
4918c2ecf20Sopenharmony_ci/* Take a basic firmware command structure, format it
4928c2ecf20Sopenharmony_ci * and forward it for processing
4938c2ecf20Sopenharmony_ci */
4948c2ecf20Sopenharmony_cistatic int cx24117_cmd_execute_nolock(struct dvb_frontend *fe,
4958c2ecf20Sopenharmony_ci	struct cx24117_cmd *cmd)
4968c2ecf20Sopenharmony_ci{
4978c2ecf20Sopenharmony_ci	struct cx24117_state *state = fe->demodulator_priv;
4988c2ecf20Sopenharmony_ci	int i, ret;
4998c2ecf20Sopenharmony_ci
5008c2ecf20Sopenharmony_ci	dev_dbg(&state->priv->i2c->dev, "%s() demod%d\n",
5018c2ecf20Sopenharmony_ci		__func__, state->demod);
5028c2ecf20Sopenharmony_ci
5038c2ecf20Sopenharmony_ci	/* Load the firmware if required */
5048c2ecf20Sopenharmony_ci	ret = cx24117_firmware_ondemand(fe);
5058c2ecf20Sopenharmony_ci	if (ret != 0)
5068c2ecf20Sopenharmony_ci		return ret;
5078c2ecf20Sopenharmony_ci
5088c2ecf20Sopenharmony_ci	/* Write the command */
5098c2ecf20Sopenharmony_ci	cx24117_writecmd(state, cmd);
5108c2ecf20Sopenharmony_ci
5118c2ecf20Sopenharmony_ci	/* Start execution and wait for cmd to terminate */
5128c2ecf20Sopenharmony_ci	cx24117_writereg(state, CX24117_REG_EXECUTE, 0x01);
5138c2ecf20Sopenharmony_ci	i = 0;
5148c2ecf20Sopenharmony_ci	while (cx24117_readreg(state, CX24117_REG_EXECUTE)) {
5158c2ecf20Sopenharmony_ci		msleep(20);
5168c2ecf20Sopenharmony_ci		if (i++ > 40) {
5178c2ecf20Sopenharmony_ci			/* Avoid looping forever if the firmware does
5188c2ecf20Sopenharmony_ci				not respond */
5198c2ecf20Sopenharmony_ci			dev_warn(&state->priv->i2c->dev,
5208c2ecf20Sopenharmony_ci				"%s() Firmware not responding\n", __func__);
5218c2ecf20Sopenharmony_ci			return -EIO;
5228c2ecf20Sopenharmony_ci		}
5238c2ecf20Sopenharmony_ci	}
5248c2ecf20Sopenharmony_ci	return 0;
5258c2ecf20Sopenharmony_ci}
5268c2ecf20Sopenharmony_ci
5278c2ecf20Sopenharmony_cistatic int cx24117_cmd_execute(struct dvb_frontend *fe, struct cx24117_cmd *cmd)
5288c2ecf20Sopenharmony_ci{
5298c2ecf20Sopenharmony_ci	struct cx24117_state *state = fe->demodulator_priv;
5308c2ecf20Sopenharmony_ci	int ret;
5318c2ecf20Sopenharmony_ci
5328c2ecf20Sopenharmony_ci	mutex_lock(&state->priv->fe_lock);
5338c2ecf20Sopenharmony_ci	ret = cx24117_cmd_execute_nolock(fe, cmd);
5348c2ecf20Sopenharmony_ci	mutex_unlock(&state->priv->fe_lock);
5358c2ecf20Sopenharmony_ci
5368c2ecf20Sopenharmony_ci	return ret;
5378c2ecf20Sopenharmony_ci}
5388c2ecf20Sopenharmony_ci
5398c2ecf20Sopenharmony_cistatic int cx24117_load_firmware(struct dvb_frontend *fe,
5408c2ecf20Sopenharmony_ci	const struct firmware *fw)
5418c2ecf20Sopenharmony_ci{
5428c2ecf20Sopenharmony_ci	struct cx24117_state *state = fe->demodulator_priv;
5438c2ecf20Sopenharmony_ci	struct cx24117_cmd cmd;
5448c2ecf20Sopenharmony_ci	int i, ret;
5458c2ecf20Sopenharmony_ci	unsigned char vers[4];
5468c2ecf20Sopenharmony_ci
5478c2ecf20Sopenharmony_ci	struct i2c_msg msg;
5488c2ecf20Sopenharmony_ci	u8 *buf;
5498c2ecf20Sopenharmony_ci
5508c2ecf20Sopenharmony_ci	dev_dbg(&state->priv->i2c->dev,
5518c2ecf20Sopenharmony_ci		"%s() demod%d FW is %zu bytes (%02x %02x .. %02x %02x)\n",
5528c2ecf20Sopenharmony_ci		__func__, state->demod, fw->size, fw->data[0], fw->data[1],
5538c2ecf20Sopenharmony_ci		fw->data[fw->size - 2], fw->data[fw->size - 1]);
5548c2ecf20Sopenharmony_ci
5558c2ecf20Sopenharmony_ci	cx24117_writereg(state, 0xea, 0x00);
5568c2ecf20Sopenharmony_ci	cx24117_writereg(state, 0xea, 0x01);
5578c2ecf20Sopenharmony_ci	cx24117_writereg(state, 0xea, 0x00);
5588c2ecf20Sopenharmony_ci
5598c2ecf20Sopenharmony_ci	cx24117_writereg(state, 0xce, 0x92);
5608c2ecf20Sopenharmony_ci
5618c2ecf20Sopenharmony_ci	cx24117_writereg(state, 0xfb, 0x00);
5628c2ecf20Sopenharmony_ci	cx24117_writereg(state, 0xfc, 0x00);
5638c2ecf20Sopenharmony_ci
5648c2ecf20Sopenharmony_ci	cx24117_writereg(state, 0xc3, 0x04);
5658c2ecf20Sopenharmony_ci	cx24117_writereg(state, 0xc4, 0x04);
5668c2ecf20Sopenharmony_ci
5678c2ecf20Sopenharmony_ci	cx24117_writereg(state, 0xce, 0x00);
5688c2ecf20Sopenharmony_ci	cx24117_writereg(state, 0xcf, 0x00);
5698c2ecf20Sopenharmony_ci
5708c2ecf20Sopenharmony_ci	cx24117_writereg(state, 0xea, 0x00);
5718c2ecf20Sopenharmony_ci	cx24117_writereg(state, 0xeb, 0x0c);
5728c2ecf20Sopenharmony_ci	cx24117_writereg(state, 0xec, 0x06);
5738c2ecf20Sopenharmony_ci	cx24117_writereg(state, 0xed, 0x05);
5748c2ecf20Sopenharmony_ci	cx24117_writereg(state, 0xee, 0x03);
5758c2ecf20Sopenharmony_ci	cx24117_writereg(state, 0xef, 0x05);
5768c2ecf20Sopenharmony_ci
5778c2ecf20Sopenharmony_ci	cx24117_writereg(state, 0xf3, 0x03);
5788c2ecf20Sopenharmony_ci	cx24117_writereg(state, 0xf4, 0x44);
5798c2ecf20Sopenharmony_ci
5808c2ecf20Sopenharmony_ci	cx24117_writereg(state, CX24117_REG_RATEDIV0, 0x04);
5818c2ecf20Sopenharmony_ci	cx24117_writereg(state, CX24117_REG_CLKDIV0, 0x02);
5828c2ecf20Sopenharmony_ci
5838c2ecf20Sopenharmony_ci	cx24117_writereg(state, CX24117_REG_RATEDIV1, 0x04);
5848c2ecf20Sopenharmony_ci	cx24117_writereg(state, CX24117_REG_CLKDIV1, 0x02);
5858c2ecf20Sopenharmony_ci
5868c2ecf20Sopenharmony_ci	cx24117_writereg(state, 0xf2, 0x04);
5878c2ecf20Sopenharmony_ci	cx24117_writereg(state, 0xe8, 0x02);
5888c2ecf20Sopenharmony_ci	cx24117_writereg(state, 0xea, 0x01);
5898c2ecf20Sopenharmony_ci	cx24117_writereg(state, 0xc8, 0x00);
5908c2ecf20Sopenharmony_ci	cx24117_writereg(state, 0xc9, 0x00);
5918c2ecf20Sopenharmony_ci	cx24117_writereg(state, 0xca, 0x00);
5928c2ecf20Sopenharmony_ci	cx24117_writereg(state, 0xcb, 0x00);
5938c2ecf20Sopenharmony_ci	cx24117_writereg(state, 0xcc, 0x00);
5948c2ecf20Sopenharmony_ci	cx24117_writereg(state, 0xcd, 0x00);
5958c2ecf20Sopenharmony_ci	cx24117_writereg(state, 0xe4, 0x03);
5968c2ecf20Sopenharmony_ci	cx24117_writereg(state, 0xeb, 0x0a);
5978c2ecf20Sopenharmony_ci
5988c2ecf20Sopenharmony_ci	cx24117_writereg(state, 0xfb, 0x00);
5998c2ecf20Sopenharmony_ci	cx24117_writereg(state, 0xe0, 0x76);
6008c2ecf20Sopenharmony_ci	cx24117_writereg(state, 0xf7, 0x81);
6018c2ecf20Sopenharmony_ci	cx24117_writereg(state, 0xf8, 0x00);
6028c2ecf20Sopenharmony_ci	cx24117_writereg(state, 0xf9, 0x00);
6038c2ecf20Sopenharmony_ci
6048c2ecf20Sopenharmony_ci	buf = kmalloc(fw->size + 1, GFP_KERNEL);
6058c2ecf20Sopenharmony_ci	if (buf == NULL) {
6068c2ecf20Sopenharmony_ci		state->priv->skip_fw_load = 0;
6078c2ecf20Sopenharmony_ci		return -ENOMEM;
6088c2ecf20Sopenharmony_ci	}
6098c2ecf20Sopenharmony_ci
6108c2ecf20Sopenharmony_ci	/* fw upload reg */
6118c2ecf20Sopenharmony_ci	buf[0] = 0xfa;
6128c2ecf20Sopenharmony_ci	memcpy(&buf[1], fw->data, fw->size);
6138c2ecf20Sopenharmony_ci
6148c2ecf20Sopenharmony_ci	/* prepare i2c message to send */
6158c2ecf20Sopenharmony_ci	msg.addr = state->priv->demod_address;
6168c2ecf20Sopenharmony_ci	msg.flags = 0;
6178c2ecf20Sopenharmony_ci	msg.len = fw->size + 1;
6188c2ecf20Sopenharmony_ci	msg.buf = buf;
6198c2ecf20Sopenharmony_ci
6208c2ecf20Sopenharmony_ci	/* send fw */
6218c2ecf20Sopenharmony_ci	ret = i2c_transfer(state->priv->i2c, &msg, 1);
6228c2ecf20Sopenharmony_ci	if (ret < 0) {
6238c2ecf20Sopenharmony_ci		kfree(buf);
6248c2ecf20Sopenharmony_ci		return ret;
6258c2ecf20Sopenharmony_ci	}
6268c2ecf20Sopenharmony_ci
6278c2ecf20Sopenharmony_ci	kfree(buf);
6288c2ecf20Sopenharmony_ci
6298c2ecf20Sopenharmony_ci	cx24117_writereg(state, 0xf7, 0x0c);
6308c2ecf20Sopenharmony_ci	cx24117_writereg(state, 0xe0, 0x00);
6318c2ecf20Sopenharmony_ci
6328c2ecf20Sopenharmony_ci	/* Init demodulator */
6338c2ecf20Sopenharmony_ci	cmd.args[0] = CMD_DEMODINIT;
6348c2ecf20Sopenharmony_ci	cmd.args[1] = 0x00;
6358c2ecf20Sopenharmony_ci	cmd.args[2] = 0x01;
6368c2ecf20Sopenharmony_ci	cmd.args[3] = 0x00;
6378c2ecf20Sopenharmony_ci	cmd.len = 4;
6388c2ecf20Sopenharmony_ci	ret = cx24117_cmd_execute_nolock(fe, &cmd);
6398c2ecf20Sopenharmony_ci	if (ret != 0)
6408c2ecf20Sopenharmony_ci		goto error;
6418c2ecf20Sopenharmony_ci
6428c2ecf20Sopenharmony_ci	/* Set VCO frequency */
6438c2ecf20Sopenharmony_ci	cmd.args[0] = CMD_SET_VCOFREQ;
6448c2ecf20Sopenharmony_ci	cmd.args[1] = 0x06;
6458c2ecf20Sopenharmony_ci	cmd.args[2] = 0x2b;
6468c2ecf20Sopenharmony_ci	cmd.args[3] = 0xd8;
6478c2ecf20Sopenharmony_ci	cmd.args[4] = 0xa5;
6488c2ecf20Sopenharmony_ci	cmd.args[5] = 0xee;
6498c2ecf20Sopenharmony_ci	cmd.args[6] = 0x03;
6508c2ecf20Sopenharmony_ci	cmd.args[7] = 0x9d;
6518c2ecf20Sopenharmony_ci	cmd.args[8] = 0xfc;
6528c2ecf20Sopenharmony_ci	cmd.args[9] = 0x06;
6538c2ecf20Sopenharmony_ci	cmd.args[10] = 0x02;
6548c2ecf20Sopenharmony_ci	cmd.args[11] = 0x9d;
6558c2ecf20Sopenharmony_ci	cmd.args[12] = 0xfc;
6568c2ecf20Sopenharmony_ci	cmd.len = 13;
6578c2ecf20Sopenharmony_ci	ret = cx24117_cmd_execute_nolock(fe, &cmd);
6588c2ecf20Sopenharmony_ci	if (ret != 0)
6598c2ecf20Sopenharmony_ci		goto error;
6608c2ecf20Sopenharmony_ci
6618c2ecf20Sopenharmony_ci	/* Tuner init */
6628c2ecf20Sopenharmony_ci	cmd.args[0] = CMD_TUNERINIT;
6638c2ecf20Sopenharmony_ci	cmd.args[1] = 0x00;
6648c2ecf20Sopenharmony_ci	cmd.args[2] = 0x01;
6658c2ecf20Sopenharmony_ci	cmd.args[3] = 0x00;
6668c2ecf20Sopenharmony_ci	cmd.args[4] = 0x00;
6678c2ecf20Sopenharmony_ci	cmd.args[5] = 0x01;
6688c2ecf20Sopenharmony_ci	cmd.args[6] = 0x01;
6698c2ecf20Sopenharmony_ci	cmd.args[7] = 0x01;
6708c2ecf20Sopenharmony_ci	cmd.args[8] = 0x00;
6718c2ecf20Sopenharmony_ci	cmd.args[9] = 0x05;
6728c2ecf20Sopenharmony_ci	cmd.args[10] = 0x02;
6738c2ecf20Sopenharmony_ci	cmd.args[11] = 0x02;
6748c2ecf20Sopenharmony_ci	cmd.args[12] = 0x00;
6758c2ecf20Sopenharmony_ci	cmd.len = 13;
6768c2ecf20Sopenharmony_ci	ret = cx24117_cmd_execute_nolock(fe, &cmd);
6778c2ecf20Sopenharmony_ci	if (ret != 0)
6788c2ecf20Sopenharmony_ci		goto error;
6798c2ecf20Sopenharmony_ci
6808c2ecf20Sopenharmony_ci	/* Global MPEG config */
6818c2ecf20Sopenharmony_ci	cmd.args[0] = CMD_GLOBAL_MPEGCFG;
6828c2ecf20Sopenharmony_ci	cmd.args[1] = 0x00;
6838c2ecf20Sopenharmony_ci	cmd.args[2] = 0x00;
6848c2ecf20Sopenharmony_ci	cmd.args[3] = 0x00;
6858c2ecf20Sopenharmony_ci	cmd.args[4] = 0x01;
6868c2ecf20Sopenharmony_ci	cmd.args[5] = 0x00;
6878c2ecf20Sopenharmony_ci	cmd.len = 6;
6888c2ecf20Sopenharmony_ci	ret = cx24117_cmd_execute_nolock(fe, &cmd);
6898c2ecf20Sopenharmony_ci	if (ret != 0)
6908c2ecf20Sopenharmony_ci		goto error;
6918c2ecf20Sopenharmony_ci
6928c2ecf20Sopenharmony_ci	/* MPEG config for each demod */
6938c2ecf20Sopenharmony_ci	for (i = 0; i < 2; i++) {
6948c2ecf20Sopenharmony_ci		cmd.args[0] = CMD_MPEGCFG;
6958c2ecf20Sopenharmony_ci		cmd.args[1] = (u8) i;
6968c2ecf20Sopenharmony_ci		cmd.args[2] = 0x00;
6978c2ecf20Sopenharmony_ci		cmd.args[3] = 0x05;
6988c2ecf20Sopenharmony_ci		cmd.args[4] = 0x00;
6998c2ecf20Sopenharmony_ci		cmd.args[5] = 0x00;
7008c2ecf20Sopenharmony_ci		cmd.args[6] = 0x55;
7018c2ecf20Sopenharmony_ci		cmd.args[7] = 0x00;
7028c2ecf20Sopenharmony_ci		cmd.len = 8;
7038c2ecf20Sopenharmony_ci		ret = cx24117_cmd_execute_nolock(fe, &cmd);
7048c2ecf20Sopenharmony_ci		if (ret != 0)
7058c2ecf20Sopenharmony_ci			goto error;
7068c2ecf20Sopenharmony_ci	}
7078c2ecf20Sopenharmony_ci
7088c2ecf20Sopenharmony_ci	cx24117_writereg(state, 0xce, 0xc0);
7098c2ecf20Sopenharmony_ci	cx24117_writereg(state, 0xcf, 0x00);
7108c2ecf20Sopenharmony_ci	cx24117_writereg(state, 0xe5, 0x04);
7118c2ecf20Sopenharmony_ci
7128c2ecf20Sopenharmony_ci	/* Get firmware version */
7138c2ecf20Sopenharmony_ci	cmd.args[0] = CMD_FWVERSION;
7148c2ecf20Sopenharmony_ci	cmd.len = 2;
7158c2ecf20Sopenharmony_ci	for (i = 0; i < 4; i++) {
7168c2ecf20Sopenharmony_ci		cmd.args[1] = i;
7178c2ecf20Sopenharmony_ci		ret = cx24117_cmd_execute_nolock(fe, &cmd);
7188c2ecf20Sopenharmony_ci		if (ret != 0)
7198c2ecf20Sopenharmony_ci			goto error;
7208c2ecf20Sopenharmony_ci		vers[i] = cx24117_readreg(state, 0x33);
7218c2ecf20Sopenharmony_ci	}
7228c2ecf20Sopenharmony_ci	dev_info(&state->priv->i2c->dev,
7238c2ecf20Sopenharmony_ci		"%s: FW version %i.%i.%i.%i\n", __func__,
7248c2ecf20Sopenharmony_ci		vers[0], vers[1], vers[2], vers[3]);
7258c2ecf20Sopenharmony_ci	return 0;
7268c2ecf20Sopenharmony_cierror:
7278c2ecf20Sopenharmony_ci	state->priv->skip_fw_load = 0;
7288c2ecf20Sopenharmony_ci	dev_err(&state->priv->i2c->dev, "%s() Error running FW.\n", __func__);
7298c2ecf20Sopenharmony_ci	return ret;
7308c2ecf20Sopenharmony_ci}
7318c2ecf20Sopenharmony_ci
7328c2ecf20Sopenharmony_cistatic int cx24117_read_status(struct dvb_frontend *fe, enum fe_status *status)
7338c2ecf20Sopenharmony_ci{
7348c2ecf20Sopenharmony_ci	struct cx24117_state *state = fe->demodulator_priv;
7358c2ecf20Sopenharmony_ci	int lock;
7368c2ecf20Sopenharmony_ci
7378c2ecf20Sopenharmony_ci	lock = cx24117_readreg(state,
7388c2ecf20Sopenharmony_ci		(state->demod == 0) ? CX24117_REG_SSTATUS0 :
7398c2ecf20Sopenharmony_ci				      CX24117_REG_SSTATUS1) &
7408c2ecf20Sopenharmony_ci		CX24117_STATUS_MASK;
7418c2ecf20Sopenharmony_ci
7428c2ecf20Sopenharmony_ci	dev_dbg(&state->priv->i2c->dev, "%s() demod%d status = 0x%02x\n",
7438c2ecf20Sopenharmony_ci		__func__, state->demod, lock);
7448c2ecf20Sopenharmony_ci
7458c2ecf20Sopenharmony_ci	*status = 0;
7468c2ecf20Sopenharmony_ci
7478c2ecf20Sopenharmony_ci	if (lock & CX24117_HAS_SIGNAL)
7488c2ecf20Sopenharmony_ci		*status |= FE_HAS_SIGNAL;
7498c2ecf20Sopenharmony_ci	if (lock & CX24117_HAS_CARRIER)
7508c2ecf20Sopenharmony_ci		*status |= FE_HAS_CARRIER;
7518c2ecf20Sopenharmony_ci	if (lock & CX24117_HAS_VITERBI)
7528c2ecf20Sopenharmony_ci		*status |= FE_HAS_VITERBI;
7538c2ecf20Sopenharmony_ci	if (lock & CX24117_HAS_SYNCLOCK)
7548c2ecf20Sopenharmony_ci		*status |= FE_HAS_SYNC | FE_HAS_LOCK;
7558c2ecf20Sopenharmony_ci
7568c2ecf20Sopenharmony_ci	return 0;
7578c2ecf20Sopenharmony_ci}
7588c2ecf20Sopenharmony_ci
7598c2ecf20Sopenharmony_cistatic int cx24117_read_ber(struct dvb_frontend *fe, u32 *ber)
7608c2ecf20Sopenharmony_ci{
7618c2ecf20Sopenharmony_ci	struct cx24117_state *state = fe->demodulator_priv;
7628c2ecf20Sopenharmony_ci	int ret;
7638c2ecf20Sopenharmony_ci	u8 buf[4];
7648c2ecf20Sopenharmony_ci	u8 base_reg = (state->demod == 0) ?
7658c2ecf20Sopenharmony_ci			CX24117_REG_BER4_0 :
7668c2ecf20Sopenharmony_ci			CX24117_REG_BER4_1;
7678c2ecf20Sopenharmony_ci
7688c2ecf20Sopenharmony_ci	ret = cx24117_readregN(state, base_reg, buf, 4);
7698c2ecf20Sopenharmony_ci	if (ret != 0)
7708c2ecf20Sopenharmony_ci		return ret;
7718c2ecf20Sopenharmony_ci
7728c2ecf20Sopenharmony_ci	*ber = (buf[0] << 24) | (buf[1] << 16) |
7738c2ecf20Sopenharmony_ci		(buf[1] << 8) | buf[0];
7748c2ecf20Sopenharmony_ci
7758c2ecf20Sopenharmony_ci	dev_dbg(&state->priv->i2c->dev, "%s() demod%d ber=0x%04x\n",
7768c2ecf20Sopenharmony_ci		__func__, state->demod, *ber);
7778c2ecf20Sopenharmony_ci
7788c2ecf20Sopenharmony_ci	return 0;
7798c2ecf20Sopenharmony_ci}
7808c2ecf20Sopenharmony_ci
7818c2ecf20Sopenharmony_cistatic int cx24117_read_signal_strength(struct dvb_frontend *fe,
7828c2ecf20Sopenharmony_ci	u16 *signal_strength)
7838c2ecf20Sopenharmony_ci{
7848c2ecf20Sopenharmony_ci	struct cx24117_state *state = fe->demodulator_priv;
7858c2ecf20Sopenharmony_ci	struct cx24117_cmd cmd;
7868c2ecf20Sopenharmony_ci	int ret;
7878c2ecf20Sopenharmony_ci	u16 sig_reading;
7888c2ecf20Sopenharmony_ci	u8 buf[2];
7898c2ecf20Sopenharmony_ci	u8 reg = (state->demod == 0) ?
7908c2ecf20Sopenharmony_ci		CX24117_REG_SSTATUS0 : CX24117_REG_SSTATUS1;
7918c2ecf20Sopenharmony_ci
7928c2ecf20Sopenharmony_ci	/* Read AGC accumulator register */
7938c2ecf20Sopenharmony_ci	cmd.args[0] = CMD_GET_AGCACC;
7948c2ecf20Sopenharmony_ci	cmd.args[1] = (u8) state->demod;
7958c2ecf20Sopenharmony_ci	cmd.len = 2;
7968c2ecf20Sopenharmony_ci	ret = cx24117_cmd_execute(fe, &cmd);
7978c2ecf20Sopenharmony_ci	if (ret != 0)
7988c2ecf20Sopenharmony_ci		return ret;
7998c2ecf20Sopenharmony_ci
8008c2ecf20Sopenharmony_ci	ret = cx24117_readregN(state, reg, buf, 2);
8018c2ecf20Sopenharmony_ci	if (ret != 0)
8028c2ecf20Sopenharmony_ci		return ret;
8038c2ecf20Sopenharmony_ci	sig_reading = ((buf[0] & CX24117_SIGNAL_MASK) << 2) | buf[1];
8048c2ecf20Sopenharmony_ci
8058c2ecf20Sopenharmony_ci	*signal_strength = -100 * sig_reading + 94324;
8068c2ecf20Sopenharmony_ci
8078c2ecf20Sopenharmony_ci	dev_dbg(&state->priv->i2c->dev,
8088c2ecf20Sopenharmony_ci		"%s() demod%d raw / cooked = 0x%04x / 0x%04x\n",
8098c2ecf20Sopenharmony_ci		__func__, state->demod, sig_reading, *signal_strength);
8108c2ecf20Sopenharmony_ci
8118c2ecf20Sopenharmony_ci	return 0;
8128c2ecf20Sopenharmony_ci}
8138c2ecf20Sopenharmony_ci
8148c2ecf20Sopenharmony_cistatic int cx24117_read_snr(struct dvb_frontend *fe, u16 *snr)
8158c2ecf20Sopenharmony_ci{
8168c2ecf20Sopenharmony_ci	struct cx24117_state *state = fe->demodulator_priv;
8178c2ecf20Sopenharmony_ci	int ret;
8188c2ecf20Sopenharmony_ci	u8 buf[2];
8198c2ecf20Sopenharmony_ci	u8 reg = (state->demod == 0) ?
8208c2ecf20Sopenharmony_ci		CX24117_REG_QUALITY2_0 : CX24117_REG_QUALITY2_1;
8218c2ecf20Sopenharmony_ci
8228c2ecf20Sopenharmony_ci	ret = cx24117_readregN(state, reg, buf, 2);
8238c2ecf20Sopenharmony_ci	if (ret != 0)
8248c2ecf20Sopenharmony_ci		return ret;
8258c2ecf20Sopenharmony_ci
8268c2ecf20Sopenharmony_ci	*snr = (buf[0] << 8) | buf[1];
8278c2ecf20Sopenharmony_ci
8288c2ecf20Sopenharmony_ci	dev_dbg(&state->priv->i2c->dev,
8298c2ecf20Sopenharmony_ci		"%s() demod%d snr = 0x%04x\n",
8308c2ecf20Sopenharmony_ci		__func__, state->demod, *snr);
8318c2ecf20Sopenharmony_ci
8328c2ecf20Sopenharmony_ci	return ret;
8338c2ecf20Sopenharmony_ci}
8348c2ecf20Sopenharmony_ci
8358c2ecf20Sopenharmony_cistatic int cx24117_read_ucblocks(struct dvb_frontend *fe, u32 *ucblocks)
8368c2ecf20Sopenharmony_ci{
8378c2ecf20Sopenharmony_ci	struct cx24117_state *state = fe->demodulator_priv;
8388c2ecf20Sopenharmony_ci	enum fe_delivery_system delsys = fe->dtv_property_cache.delivery_system;
8398c2ecf20Sopenharmony_ci	int ret;
8408c2ecf20Sopenharmony_ci	u8 buf[2];
8418c2ecf20Sopenharmony_ci	u8 reg = (state->demod == 0) ?
8428c2ecf20Sopenharmony_ci		CX24117_REG_DVBS_UCB2_0 :
8438c2ecf20Sopenharmony_ci		CX24117_REG_DVBS_UCB2_1;
8448c2ecf20Sopenharmony_ci
8458c2ecf20Sopenharmony_ci	switch (delsys) {
8468c2ecf20Sopenharmony_ci	case SYS_DVBS:
8478c2ecf20Sopenharmony_ci		break;
8488c2ecf20Sopenharmony_ci	case SYS_DVBS2:
8498c2ecf20Sopenharmony_ci		reg += (CX24117_REG_DVBS2_UCB2_0 - CX24117_REG_DVBS_UCB2_0);
8508c2ecf20Sopenharmony_ci		break;
8518c2ecf20Sopenharmony_ci	default:
8528c2ecf20Sopenharmony_ci		return -EINVAL;
8538c2ecf20Sopenharmony_ci	}
8548c2ecf20Sopenharmony_ci
8558c2ecf20Sopenharmony_ci	ret = cx24117_readregN(state, reg, buf, 2);
8568c2ecf20Sopenharmony_ci	if (ret != 0)
8578c2ecf20Sopenharmony_ci		return ret;
8588c2ecf20Sopenharmony_ci	*ucblocks = (buf[0] << 8) | buf[1];
8598c2ecf20Sopenharmony_ci
8608c2ecf20Sopenharmony_ci	dev_dbg(&state->priv->i2c->dev, "%s() demod%d ucb=0x%04x\n",
8618c2ecf20Sopenharmony_ci		__func__, state->demod, *ucblocks);
8628c2ecf20Sopenharmony_ci
8638c2ecf20Sopenharmony_ci	return 0;
8648c2ecf20Sopenharmony_ci}
8658c2ecf20Sopenharmony_ci
8668c2ecf20Sopenharmony_ci/* Overwrite the current tuning params, we are about to tune */
8678c2ecf20Sopenharmony_cistatic void cx24117_clone_params(struct dvb_frontend *fe)
8688c2ecf20Sopenharmony_ci{
8698c2ecf20Sopenharmony_ci	struct cx24117_state *state = fe->demodulator_priv;
8708c2ecf20Sopenharmony_ci	state->dcur = state->dnxt;
8718c2ecf20Sopenharmony_ci}
8728c2ecf20Sopenharmony_ci
8738c2ecf20Sopenharmony_ci/* Wait for LNB */
8748c2ecf20Sopenharmony_cistatic int cx24117_wait_for_lnb(struct dvb_frontend *fe)
8758c2ecf20Sopenharmony_ci{
8768c2ecf20Sopenharmony_ci	struct cx24117_state *state = fe->demodulator_priv;
8778c2ecf20Sopenharmony_ci	int i;
8788c2ecf20Sopenharmony_ci	u8 val, reg = (state->demod == 0) ? CX24117_REG_QSTATUS0 :
8798c2ecf20Sopenharmony_ci					    CX24117_REG_QSTATUS1;
8808c2ecf20Sopenharmony_ci
8818c2ecf20Sopenharmony_ci	dev_dbg(&state->priv->i2c->dev, "%s() demod%d qstatus = 0x%02x\n",
8828c2ecf20Sopenharmony_ci		__func__, state->demod, cx24117_readreg(state, reg));
8838c2ecf20Sopenharmony_ci
8848c2ecf20Sopenharmony_ci	/* Wait for up to 300 ms */
8858c2ecf20Sopenharmony_ci	for (i = 0; i < 10; i++) {
8868c2ecf20Sopenharmony_ci		val = cx24117_readreg(state, reg) & 0x01;
8878c2ecf20Sopenharmony_ci		if (val != 0)
8888c2ecf20Sopenharmony_ci			return 0;
8898c2ecf20Sopenharmony_ci		msleep(30);
8908c2ecf20Sopenharmony_ci	}
8918c2ecf20Sopenharmony_ci
8928c2ecf20Sopenharmony_ci	dev_warn(&state->priv->i2c->dev, "%s: demod%d LNB not ready\n",
8938c2ecf20Sopenharmony_ci		KBUILD_MODNAME, state->demod);
8948c2ecf20Sopenharmony_ci
8958c2ecf20Sopenharmony_ci	return -ETIMEDOUT; /* -EBUSY ? */
8968c2ecf20Sopenharmony_ci}
8978c2ecf20Sopenharmony_ci
8988c2ecf20Sopenharmony_cistatic int cx24117_set_voltage(struct dvb_frontend *fe,
8998c2ecf20Sopenharmony_ci			       enum fe_sec_voltage voltage)
9008c2ecf20Sopenharmony_ci{
9018c2ecf20Sopenharmony_ci	struct cx24117_state *state = fe->demodulator_priv;
9028c2ecf20Sopenharmony_ci	struct cx24117_cmd cmd;
9038c2ecf20Sopenharmony_ci	int ret;
9048c2ecf20Sopenharmony_ci	u8 reg = (state->demod == 0) ? 0x10 : 0x20;
9058c2ecf20Sopenharmony_ci
9068c2ecf20Sopenharmony_ci	dev_dbg(&state->priv->i2c->dev, "%s() demod%d %s\n",
9078c2ecf20Sopenharmony_ci		__func__, state->demod,
9088c2ecf20Sopenharmony_ci		voltage == SEC_VOLTAGE_13 ? "SEC_VOLTAGE_13" :
9098c2ecf20Sopenharmony_ci		voltage == SEC_VOLTAGE_18 ? "SEC_VOLTAGE_18" :
9108c2ecf20Sopenharmony_ci		"SEC_VOLTAGE_OFF");
9118c2ecf20Sopenharmony_ci
9128c2ecf20Sopenharmony_ci	/* Prepare a set GPIO logic level CMD */
9138c2ecf20Sopenharmony_ci	cmd.args[0] = CMD_SET_GPIOOUT;
9148c2ecf20Sopenharmony_ci	cmd.args[2] = reg; /* mask */
9158c2ecf20Sopenharmony_ci	cmd.len = 3;
9168c2ecf20Sopenharmony_ci
9178c2ecf20Sopenharmony_ci	if ((voltage == SEC_VOLTAGE_13) ||
9188c2ecf20Sopenharmony_ci	    (voltage == SEC_VOLTAGE_18)) {
9198c2ecf20Sopenharmony_ci		/* power on LNB */
9208c2ecf20Sopenharmony_ci		cmd.args[1] = reg;
9218c2ecf20Sopenharmony_ci		ret = cx24117_cmd_execute(fe, &cmd);
9228c2ecf20Sopenharmony_ci		if (ret != 0)
9238c2ecf20Sopenharmony_ci			return ret;
9248c2ecf20Sopenharmony_ci
9258c2ecf20Sopenharmony_ci		ret = cx24117_wait_for_lnb(fe);
9268c2ecf20Sopenharmony_ci		if (ret != 0)
9278c2ecf20Sopenharmony_ci			return ret;
9288c2ecf20Sopenharmony_ci
9298c2ecf20Sopenharmony_ci		/* Wait for voltage/min repeat delay */
9308c2ecf20Sopenharmony_ci		msleep(100);
9318c2ecf20Sopenharmony_ci
9328c2ecf20Sopenharmony_ci		/* Set 13V/18V select pin */
9338c2ecf20Sopenharmony_ci		cmd.args[0] = CMD_LNBDCLEVEL;
9348c2ecf20Sopenharmony_ci		cmd.args[1] = state->demod ? 0 : 1;
9358c2ecf20Sopenharmony_ci		cmd.args[2] = (voltage == SEC_VOLTAGE_18 ? 0x01 : 0x00);
9368c2ecf20Sopenharmony_ci		cmd.len = 3;
9378c2ecf20Sopenharmony_ci		ret = cx24117_cmd_execute(fe, &cmd);
9388c2ecf20Sopenharmony_ci
9398c2ecf20Sopenharmony_ci		/* Min delay time before DiSEqC send */
9408c2ecf20Sopenharmony_ci		msleep(20);
9418c2ecf20Sopenharmony_ci	} else {
9428c2ecf20Sopenharmony_ci		/* power off LNB */
9438c2ecf20Sopenharmony_ci		cmd.args[1] = 0x00;
9448c2ecf20Sopenharmony_ci		ret = cx24117_cmd_execute(fe, &cmd);
9458c2ecf20Sopenharmony_ci	}
9468c2ecf20Sopenharmony_ci
9478c2ecf20Sopenharmony_ci	return ret;
9488c2ecf20Sopenharmony_ci}
9498c2ecf20Sopenharmony_ci
9508c2ecf20Sopenharmony_cistatic int cx24117_set_tone(struct dvb_frontend *fe,
9518c2ecf20Sopenharmony_ci			    enum fe_sec_tone_mode tone)
9528c2ecf20Sopenharmony_ci{
9538c2ecf20Sopenharmony_ci	struct cx24117_state *state = fe->demodulator_priv;
9548c2ecf20Sopenharmony_ci	struct cx24117_cmd cmd;
9558c2ecf20Sopenharmony_ci	int ret;
9568c2ecf20Sopenharmony_ci
9578c2ecf20Sopenharmony_ci	dev_dbg(&state->priv->i2c->dev, "%s(%d) demod%d\n",
9588c2ecf20Sopenharmony_ci		__func__, state->demod, tone);
9598c2ecf20Sopenharmony_ci	if ((tone != SEC_TONE_ON) && (tone != SEC_TONE_OFF)) {
9608c2ecf20Sopenharmony_ci		dev_warn(&state->priv->i2c->dev, "%s: demod%d invalid tone=%d\n",
9618c2ecf20Sopenharmony_ci			KBUILD_MODNAME, state->demod, tone);
9628c2ecf20Sopenharmony_ci		return -EINVAL;
9638c2ecf20Sopenharmony_ci	}
9648c2ecf20Sopenharmony_ci
9658c2ecf20Sopenharmony_ci	/* Wait for LNB ready */
9668c2ecf20Sopenharmony_ci	ret = cx24117_wait_for_lnb(fe);
9678c2ecf20Sopenharmony_ci	if (ret != 0)
9688c2ecf20Sopenharmony_ci		return ret;
9698c2ecf20Sopenharmony_ci
9708c2ecf20Sopenharmony_ci	/* Min delay time after DiSEqC send */
9718c2ecf20Sopenharmony_ci	msleep(20);
9728c2ecf20Sopenharmony_ci
9738c2ecf20Sopenharmony_ci	/* Set the tone */
9748c2ecf20Sopenharmony_ci	cmd.args[0] = CMD_LNBPCBCONFIG;
9758c2ecf20Sopenharmony_ci	cmd.args[1] = (state->demod ? 0 : 1);
9768c2ecf20Sopenharmony_ci	cmd.args[2] = 0x00;
9778c2ecf20Sopenharmony_ci	cmd.args[3] = 0x00;
9788c2ecf20Sopenharmony_ci	cmd.len = 5;
9798c2ecf20Sopenharmony_ci	switch (tone) {
9808c2ecf20Sopenharmony_ci	case SEC_TONE_ON:
9818c2ecf20Sopenharmony_ci		cmd.args[4] = 0x01;
9828c2ecf20Sopenharmony_ci		break;
9838c2ecf20Sopenharmony_ci	case SEC_TONE_OFF:
9848c2ecf20Sopenharmony_ci		cmd.args[4] = 0x00;
9858c2ecf20Sopenharmony_ci		break;
9868c2ecf20Sopenharmony_ci	}
9878c2ecf20Sopenharmony_ci
9888c2ecf20Sopenharmony_ci	msleep(20);
9898c2ecf20Sopenharmony_ci
9908c2ecf20Sopenharmony_ci	return cx24117_cmd_execute(fe, &cmd);
9918c2ecf20Sopenharmony_ci}
9928c2ecf20Sopenharmony_ci
9938c2ecf20Sopenharmony_ci/* Initialise DiSEqC */
9948c2ecf20Sopenharmony_cistatic int cx24117_diseqc_init(struct dvb_frontend *fe)
9958c2ecf20Sopenharmony_ci{
9968c2ecf20Sopenharmony_ci	struct cx24117_state *state = fe->demodulator_priv;
9978c2ecf20Sopenharmony_ci
9988c2ecf20Sopenharmony_ci	/* Prepare a DiSEqC command */
9998c2ecf20Sopenharmony_ci	state->dsec_cmd.args[0] = CMD_LNBSEND;
10008c2ecf20Sopenharmony_ci
10018c2ecf20Sopenharmony_ci	/* demod */
10028c2ecf20Sopenharmony_ci	state->dsec_cmd.args[CX24117_DISEQC_DEMOD] = state->demod ? 0 : 1;
10038c2ecf20Sopenharmony_ci
10048c2ecf20Sopenharmony_ci	/* DiSEqC burst */
10058c2ecf20Sopenharmony_ci	state->dsec_cmd.args[CX24117_DISEQC_BURST] = CX24117_DISEQC_MINI_A;
10068c2ecf20Sopenharmony_ci
10078c2ecf20Sopenharmony_ci	/* Unknown */
10088c2ecf20Sopenharmony_ci	state->dsec_cmd.args[CX24117_DISEQC_ARG3_2] = 0x02;
10098c2ecf20Sopenharmony_ci	state->dsec_cmd.args[CX24117_DISEQC_ARG4_0] = 0x00;
10108c2ecf20Sopenharmony_ci
10118c2ecf20Sopenharmony_ci	/* Continuation flag? */
10128c2ecf20Sopenharmony_ci	state->dsec_cmd.args[CX24117_DISEQC_ARG5_0] = 0x00;
10138c2ecf20Sopenharmony_ci
10148c2ecf20Sopenharmony_ci	/* DiSEqC message length */
10158c2ecf20Sopenharmony_ci	state->dsec_cmd.args[CX24117_DISEQC_MSGLEN] = 0x00;
10168c2ecf20Sopenharmony_ci
10178c2ecf20Sopenharmony_ci	/* Command length */
10188c2ecf20Sopenharmony_ci	state->dsec_cmd.len = 7;
10198c2ecf20Sopenharmony_ci
10208c2ecf20Sopenharmony_ci	return 0;
10218c2ecf20Sopenharmony_ci}
10228c2ecf20Sopenharmony_ci
10238c2ecf20Sopenharmony_ci/* Send DiSEqC message */
10248c2ecf20Sopenharmony_cistatic int cx24117_send_diseqc_msg(struct dvb_frontend *fe,
10258c2ecf20Sopenharmony_ci	struct dvb_diseqc_master_cmd *d)
10268c2ecf20Sopenharmony_ci{
10278c2ecf20Sopenharmony_ci	struct cx24117_state *state = fe->demodulator_priv;
10288c2ecf20Sopenharmony_ci	int i, ret;
10298c2ecf20Sopenharmony_ci
10308c2ecf20Sopenharmony_ci	/* Dump DiSEqC message */
10318c2ecf20Sopenharmony_ci	dev_dbg(&state->priv->i2c->dev, "%s: demod %d (",
10328c2ecf20Sopenharmony_ci		__func__, state->demod);
10338c2ecf20Sopenharmony_ci	for (i = 0; i < d->msg_len; i++)
10348c2ecf20Sopenharmony_ci		dev_dbg(&state->priv->i2c->dev, "0x%02x ", d->msg[i]);
10358c2ecf20Sopenharmony_ci	dev_dbg(&state->priv->i2c->dev, ")\n");
10368c2ecf20Sopenharmony_ci
10378c2ecf20Sopenharmony_ci	/* Validate length */
10388c2ecf20Sopenharmony_ci	if (d->msg_len > sizeof(d->msg))
10398c2ecf20Sopenharmony_ci		return -EINVAL;
10408c2ecf20Sopenharmony_ci
10418c2ecf20Sopenharmony_ci	/* DiSEqC message */
10428c2ecf20Sopenharmony_ci	for (i = 0; i < d->msg_len; i++)
10438c2ecf20Sopenharmony_ci		state->dsec_cmd.args[CX24117_DISEQC_MSGOFS + i] = d->msg[i];
10448c2ecf20Sopenharmony_ci
10458c2ecf20Sopenharmony_ci	/* DiSEqC message length */
10468c2ecf20Sopenharmony_ci	state->dsec_cmd.args[CX24117_DISEQC_MSGLEN] = d->msg_len;
10478c2ecf20Sopenharmony_ci
10488c2ecf20Sopenharmony_ci	/* Command length */
10498c2ecf20Sopenharmony_ci	state->dsec_cmd.len = CX24117_DISEQC_MSGOFS +
10508c2ecf20Sopenharmony_ci		state->dsec_cmd.args[CX24117_DISEQC_MSGLEN];
10518c2ecf20Sopenharmony_ci
10528c2ecf20Sopenharmony_ci	/*
10538c2ecf20Sopenharmony_ci	 * Message is sent with derived else cached burst
10548c2ecf20Sopenharmony_ci	 *
10558c2ecf20Sopenharmony_ci	 * WRITE PORT GROUP COMMAND 38
10568c2ecf20Sopenharmony_ci	 *
10578c2ecf20Sopenharmony_ci	 * 0/A/A: E0 10 38 F0..F3
10588c2ecf20Sopenharmony_ci	 * 1/B/B: E0 10 38 F4..F7
10598c2ecf20Sopenharmony_ci	 * 2/C/A: E0 10 38 F8..FB
10608c2ecf20Sopenharmony_ci	 * 3/D/B: E0 10 38 FC..FF
10618c2ecf20Sopenharmony_ci	 *
10628c2ecf20Sopenharmony_ci	 * databyte[3]= 8421:8421
10638c2ecf20Sopenharmony_ci	 *              ABCD:WXYZ
10648c2ecf20Sopenharmony_ci	 *              CLR :SET
10658c2ecf20Sopenharmony_ci	 *
10668c2ecf20Sopenharmony_ci	 *              WX= PORT SELECT 0..3    (X=TONEBURST)
10678c2ecf20Sopenharmony_ci	 *              Y = VOLTAGE             (0=13V, 1=18V)
10688c2ecf20Sopenharmony_ci	 *              Z = BAND                (0=LOW, 1=HIGH(22K))
10698c2ecf20Sopenharmony_ci	 */
10708c2ecf20Sopenharmony_ci	if (d->msg_len >= 4 && d->msg[2] == 0x38)
10718c2ecf20Sopenharmony_ci		state->dsec_cmd.args[CX24117_DISEQC_BURST] =
10728c2ecf20Sopenharmony_ci			((d->msg[3] & 4) >> 2);
10738c2ecf20Sopenharmony_ci
10748c2ecf20Sopenharmony_ci	dev_dbg(&state->priv->i2c->dev, "%s() demod%d burst=%d\n",
10758c2ecf20Sopenharmony_ci		__func__, state->demod,
10768c2ecf20Sopenharmony_ci		state->dsec_cmd.args[CX24117_DISEQC_BURST]);
10778c2ecf20Sopenharmony_ci
10788c2ecf20Sopenharmony_ci	/* Wait for LNB ready */
10798c2ecf20Sopenharmony_ci	ret = cx24117_wait_for_lnb(fe);
10808c2ecf20Sopenharmony_ci	if (ret != 0)
10818c2ecf20Sopenharmony_ci		return ret;
10828c2ecf20Sopenharmony_ci
10838c2ecf20Sopenharmony_ci	/* Wait for voltage/min repeat delay */
10848c2ecf20Sopenharmony_ci	msleep(100);
10858c2ecf20Sopenharmony_ci
10868c2ecf20Sopenharmony_ci	/* Command */
10878c2ecf20Sopenharmony_ci	ret = cx24117_cmd_execute(fe, &state->dsec_cmd);
10888c2ecf20Sopenharmony_ci	if (ret != 0)
10898c2ecf20Sopenharmony_ci		return ret;
10908c2ecf20Sopenharmony_ci	/*
10918c2ecf20Sopenharmony_ci	 * Wait for send
10928c2ecf20Sopenharmony_ci	 *
10938c2ecf20Sopenharmony_ci	 * Eutelsat spec:
10948c2ecf20Sopenharmony_ci	 * >15ms delay          + (XXX determine if FW does this, see set_tone)
10958c2ecf20Sopenharmony_ci	 *  13.5ms per byte     +
10968c2ecf20Sopenharmony_ci	 * >15ms delay          +
10978c2ecf20Sopenharmony_ci	 *  12.5ms burst        +
10988c2ecf20Sopenharmony_ci	 * >15ms delay            (XXX determine if FW does this, see set_tone)
10998c2ecf20Sopenharmony_ci	 */
11008c2ecf20Sopenharmony_ci	msleep((state->dsec_cmd.args[CX24117_DISEQC_MSGLEN] << 4) + 60);
11018c2ecf20Sopenharmony_ci
11028c2ecf20Sopenharmony_ci	return 0;
11038c2ecf20Sopenharmony_ci}
11048c2ecf20Sopenharmony_ci
11058c2ecf20Sopenharmony_ci/* Send DiSEqC burst */
11068c2ecf20Sopenharmony_cistatic int cx24117_diseqc_send_burst(struct dvb_frontend *fe,
11078c2ecf20Sopenharmony_ci	enum fe_sec_mini_cmd burst)
11088c2ecf20Sopenharmony_ci{
11098c2ecf20Sopenharmony_ci	struct cx24117_state *state = fe->demodulator_priv;
11108c2ecf20Sopenharmony_ci
11118c2ecf20Sopenharmony_ci	dev_dbg(&state->priv->i2c->dev, "%s(%d) demod=%d\n",
11128c2ecf20Sopenharmony_ci		__func__, burst, state->demod);
11138c2ecf20Sopenharmony_ci
11148c2ecf20Sopenharmony_ci	/* DiSEqC burst */
11158c2ecf20Sopenharmony_ci	if (burst == SEC_MINI_A)
11168c2ecf20Sopenharmony_ci		state->dsec_cmd.args[CX24117_DISEQC_BURST] =
11178c2ecf20Sopenharmony_ci			CX24117_DISEQC_MINI_A;
11188c2ecf20Sopenharmony_ci	else if (burst == SEC_MINI_B)
11198c2ecf20Sopenharmony_ci		state->dsec_cmd.args[CX24117_DISEQC_BURST] =
11208c2ecf20Sopenharmony_ci			CX24117_DISEQC_MINI_B;
11218c2ecf20Sopenharmony_ci	else
11228c2ecf20Sopenharmony_ci		return -EINVAL;
11238c2ecf20Sopenharmony_ci
11248c2ecf20Sopenharmony_ci	return 0;
11258c2ecf20Sopenharmony_ci}
11268c2ecf20Sopenharmony_ci
11278c2ecf20Sopenharmony_cistatic int cx24117_get_priv(struct cx24117_priv **priv,
11288c2ecf20Sopenharmony_ci	struct i2c_adapter *i2c, u8 client_address)
11298c2ecf20Sopenharmony_ci{
11308c2ecf20Sopenharmony_ci	int ret;
11318c2ecf20Sopenharmony_ci
11328c2ecf20Sopenharmony_ci	mutex_lock(&cx24117_list_mutex);
11338c2ecf20Sopenharmony_ci	ret = hybrid_tuner_request_state(struct cx24117_priv, (*priv),
11348c2ecf20Sopenharmony_ci		hybrid_tuner_instance_list, i2c, client_address, "cx24117");
11358c2ecf20Sopenharmony_ci	mutex_unlock(&cx24117_list_mutex);
11368c2ecf20Sopenharmony_ci
11378c2ecf20Sopenharmony_ci	return ret;
11388c2ecf20Sopenharmony_ci}
11398c2ecf20Sopenharmony_ci
11408c2ecf20Sopenharmony_cistatic void cx24117_release_priv(struct cx24117_priv *priv)
11418c2ecf20Sopenharmony_ci{
11428c2ecf20Sopenharmony_ci	mutex_lock(&cx24117_list_mutex);
11438c2ecf20Sopenharmony_ci	if (priv != NULL)
11448c2ecf20Sopenharmony_ci		hybrid_tuner_release_state(priv);
11458c2ecf20Sopenharmony_ci	mutex_unlock(&cx24117_list_mutex);
11468c2ecf20Sopenharmony_ci}
11478c2ecf20Sopenharmony_ci
11488c2ecf20Sopenharmony_cistatic void cx24117_release(struct dvb_frontend *fe)
11498c2ecf20Sopenharmony_ci{
11508c2ecf20Sopenharmony_ci	struct cx24117_state *state = fe->demodulator_priv;
11518c2ecf20Sopenharmony_ci	dev_dbg(&state->priv->i2c->dev, "%s demod%d\n",
11528c2ecf20Sopenharmony_ci		__func__, state->demod);
11538c2ecf20Sopenharmony_ci	cx24117_release_priv(state->priv);
11548c2ecf20Sopenharmony_ci	kfree(state);
11558c2ecf20Sopenharmony_ci}
11568c2ecf20Sopenharmony_ci
11578c2ecf20Sopenharmony_cistatic const struct dvb_frontend_ops cx24117_ops;
11588c2ecf20Sopenharmony_ci
11598c2ecf20Sopenharmony_cistruct dvb_frontend *cx24117_attach(const struct cx24117_config *config,
11608c2ecf20Sopenharmony_ci	struct i2c_adapter *i2c)
11618c2ecf20Sopenharmony_ci{
11628c2ecf20Sopenharmony_ci	struct cx24117_state *state = NULL;
11638c2ecf20Sopenharmony_ci	struct cx24117_priv *priv = NULL;
11648c2ecf20Sopenharmony_ci	int demod = 0;
11658c2ecf20Sopenharmony_ci
11668c2ecf20Sopenharmony_ci	/* get the common data struct for both demods */
11678c2ecf20Sopenharmony_ci	demod = cx24117_get_priv(&priv, i2c, config->demod_address);
11688c2ecf20Sopenharmony_ci
11698c2ecf20Sopenharmony_ci	switch (demod) {
11708c2ecf20Sopenharmony_ci	case 0:
11718c2ecf20Sopenharmony_ci		dev_err(&i2c->dev,
11728c2ecf20Sopenharmony_ci			"%s: Error attaching frontend %d\n",
11738c2ecf20Sopenharmony_ci			KBUILD_MODNAME, demod);
11748c2ecf20Sopenharmony_ci		goto error1;
11758c2ecf20Sopenharmony_ci		break;
11768c2ecf20Sopenharmony_ci	case 1:
11778c2ecf20Sopenharmony_ci		/* new priv instance */
11788c2ecf20Sopenharmony_ci		priv->i2c = i2c;
11798c2ecf20Sopenharmony_ci		priv->demod_address = config->demod_address;
11808c2ecf20Sopenharmony_ci		mutex_init(&priv->fe_lock);
11818c2ecf20Sopenharmony_ci		break;
11828c2ecf20Sopenharmony_ci	default:
11838c2ecf20Sopenharmony_ci		/* existing priv instance */
11848c2ecf20Sopenharmony_ci		break;
11858c2ecf20Sopenharmony_ci	}
11868c2ecf20Sopenharmony_ci
11878c2ecf20Sopenharmony_ci	/* allocate memory for the internal state */
11888c2ecf20Sopenharmony_ci	state = kzalloc(sizeof(struct cx24117_state), GFP_KERNEL);
11898c2ecf20Sopenharmony_ci	if (state == NULL)
11908c2ecf20Sopenharmony_ci		goto error2;
11918c2ecf20Sopenharmony_ci
11928c2ecf20Sopenharmony_ci	state->demod = demod - 1;
11938c2ecf20Sopenharmony_ci	state->priv = priv;
11948c2ecf20Sopenharmony_ci
11958c2ecf20Sopenharmony_ci	dev_info(&state->priv->i2c->dev,
11968c2ecf20Sopenharmony_ci		"%s: Attaching frontend %d\n",
11978c2ecf20Sopenharmony_ci		KBUILD_MODNAME, state->demod);
11988c2ecf20Sopenharmony_ci
11998c2ecf20Sopenharmony_ci	/* create dvb_frontend */
12008c2ecf20Sopenharmony_ci	memcpy(&state->frontend.ops, &cx24117_ops,
12018c2ecf20Sopenharmony_ci		sizeof(struct dvb_frontend_ops));
12028c2ecf20Sopenharmony_ci	state->frontend.demodulator_priv = state;
12038c2ecf20Sopenharmony_ci	return &state->frontend;
12048c2ecf20Sopenharmony_ci
12058c2ecf20Sopenharmony_cierror2:
12068c2ecf20Sopenharmony_ci	cx24117_release_priv(priv);
12078c2ecf20Sopenharmony_cierror1:
12088c2ecf20Sopenharmony_ci	return NULL;
12098c2ecf20Sopenharmony_ci}
12108c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(cx24117_attach);
12118c2ecf20Sopenharmony_ci
12128c2ecf20Sopenharmony_ci/*
12138c2ecf20Sopenharmony_ci * Initialise or wake up device
12148c2ecf20Sopenharmony_ci *
12158c2ecf20Sopenharmony_ci * Power config will reset and load initial firmware if required
12168c2ecf20Sopenharmony_ci */
12178c2ecf20Sopenharmony_cistatic int cx24117_initfe(struct dvb_frontend *fe)
12188c2ecf20Sopenharmony_ci{
12198c2ecf20Sopenharmony_ci	struct cx24117_state *state = fe->demodulator_priv;
12208c2ecf20Sopenharmony_ci	struct cx24117_cmd cmd;
12218c2ecf20Sopenharmony_ci	int ret;
12228c2ecf20Sopenharmony_ci
12238c2ecf20Sopenharmony_ci	dev_dbg(&state->priv->i2c->dev, "%s() demod%d\n",
12248c2ecf20Sopenharmony_ci		__func__, state->demod);
12258c2ecf20Sopenharmony_ci
12268c2ecf20Sopenharmony_ci	mutex_lock(&state->priv->fe_lock);
12278c2ecf20Sopenharmony_ci
12288c2ecf20Sopenharmony_ci	/* Set sleep mode off */
12298c2ecf20Sopenharmony_ci	cmd.args[0] = CMD_SET_SLEEPMODE;
12308c2ecf20Sopenharmony_ci	cmd.args[1] = (state->demod ? 1 : 0);
12318c2ecf20Sopenharmony_ci	cmd.args[2] = 0;
12328c2ecf20Sopenharmony_ci	cmd.len = 3;
12338c2ecf20Sopenharmony_ci	ret = cx24117_cmd_execute_nolock(fe, &cmd);
12348c2ecf20Sopenharmony_ci	if (ret != 0)
12358c2ecf20Sopenharmony_ci		goto exit;
12368c2ecf20Sopenharmony_ci
12378c2ecf20Sopenharmony_ci	ret = cx24117_diseqc_init(fe);
12388c2ecf20Sopenharmony_ci	if (ret != 0)
12398c2ecf20Sopenharmony_ci		goto exit;
12408c2ecf20Sopenharmony_ci
12418c2ecf20Sopenharmony_ci	/* Set BER control */
12428c2ecf20Sopenharmony_ci	cmd.args[0] = CMD_BERCTRL;
12438c2ecf20Sopenharmony_ci	cmd.args[1] = (state->demod ? 1 : 0);
12448c2ecf20Sopenharmony_ci	cmd.args[2] = 0x10;
12458c2ecf20Sopenharmony_ci	cmd.args[3] = 0x10;
12468c2ecf20Sopenharmony_ci	cmd.len = 4;
12478c2ecf20Sopenharmony_ci	ret = cx24117_cmd_execute_nolock(fe, &cmd);
12488c2ecf20Sopenharmony_ci	if (ret != 0)
12498c2ecf20Sopenharmony_ci		goto exit;
12508c2ecf20Sopenharmony_ci
12518c2ecf20Sopenharmony_ci	/* Set RS correction (enable/disable) */
12528c2ecf20Sopenharmony_ci	cmd.args[0] = CMD_ENABLERSCORR;
12538c2ecf20Sopenharmony_ci	cmd.args[1] = (state->demod ? 1 : 0);
12548c2ecf20Sopenharmony_ci	cmd.args[2] = CX24117_OCC;
12558c2ecf20Sopenharmony_ci	cmd.len = 3;
12568c2ecf20Sopenharmony_ci	ret = cx24117_cmd_execute_nolock(fe, &cmd);
12578c2ecf20Sopenharmony_ci	if (ret != 0)
12588c2ecf20Sopenharmony_ci		goto exit;
12598c2ecf20Sopenharmony_ci
12608c2ecf20Sopenharmony_ci	/* Set GPIO direction */
12618c2ecf20Sopenharmony_ci	/* Set as output - controls LNB power on/off */
12628c2ecf20Sopenharmony_ci	cmd.args[0] = CMD_SET_GPIODIR;
12638c2ecf20Sopenharmony_ci	cmd.args[1] = 0x30;
12648c2ecf20Sopenharmony_ci	cmd.args[2] = 0x30;
12658c2ecf20Sopenharmony_ci	cmd.len = 3;
12668c2ecf20Sopenharmony_ci	ret = cx24117_cmd_execute_nolock(fe, &cmd);
12678c2ecf20Sopenharmony_ci
12688c2ecf20Sopenharmony_ciexit:
12698c2ecf20Sopenharmony_ci	mutex_unlock(&state->priv->fe_lock);
12708c2ecf20Sopenharmony_ci
12718c2ecf20Sopenharmony_ci	return ret;
12728c2ecf20Sopenharmony_ci}
12738c2ecf20Sopenharmony_ci
12748c2ecf20Sopenharmony_ci/*
12758c2ecf20Sopenharmony_ci * Put device to sleep
12768c2ecf20Sopenharmony_ci */
12778c2ecf20Sopenharmony_cistatic int cx24117_sleep(struct dvb_frontend *fe)
12788c2ecf20Sopenharmony_ci{
12798c2ecf20Sopenharmony_ci	struct cx24117_state *state = fe->demodulator_priv;
12808c2ecf20Sopenharmony_ci	struct cx24117_cmd cmd;
12818c2ecf20Sopenharmony_ci
12828c2ecf20Sopenharmony_ci	dev_dbg(&state->priv->i2c->dev, "%s() demod%d\n",
12838c2ecf20Sopenharmony_ci		__func__, state->demod);
12848c2ecf20Sopenharmony_ci
12858c2ecf20Sopenharmony_ci	/* Set sleep mode on */
12868c2ecf20Sopenharmony_ci	cmd.args[0] = CMD_SET_SLEEPMODE;
12878c2ecf20Sopenharmony_ci	cmd.args[1] = (state->demod ? 1 : 0);
12888c2ecf20Sopenharmony_ci	cmd.args[2] = 1;
12898c2ecf20Sopenharmony_ci	cmd.len = 3;
12908c2ecf20Sopenharmony_ci	return cx24117_cmd_execute(fe, &cmd);
12918c2ecf20Sopenharmony_ci}
12928c2ecf20Sopenharmony_ci
12938c2ecf20Sopenharmony_ci/* dvb-core told us to tune, the tv property cache will be complete,
12948c2ecf20Sopenharmony_ci * it's safe for is to pull values and use them for tuning purposes.
12958c2ecf20Sopenharmony_ci */
12968c2ecf20Sopenharmony_cistatic int cx24117_set_frontend(struct dvb_frontend *fe)
12978c2ecf20Sopenharmony_ci{
12988c2ecf20Sopenharmony_ci	struct cx24117_state *state = fe->demodulator_priv;
12998c2ecf20Sopenharmony_ci	struct dtv_frontend_properties *c = &fe->dtv_property_cache;
13008c2ecf20Sopenharmony_ci	struct cx24117_cmd cmd;
13018c2ecf20Sopenharmony_ci	enum fe_status tunerstat;
13028c2ecf20Sopenharmony_ci	int i, status, ret, retune = 1;
13038c2ecf20Sopenharmony_ci	u8 reg_clkdiv, reg_ratediv;
13048c2ecf20Sopenharmony_ci
13058c2ecf20Sopenharmony_ci	dev_dbg(&state->priv->i2c->dev, "%s() demod%d\n",
13068c2ecf20Sopenharmony_ci		__func__, state->demod);
13078c2ecf20Sopenharmony_ci
13088c2ecf20Sopenharmony_ci	switch (c->delivery_system) {
13098c2ecf20Sopenharmony_ci	case SYS_DVBS:
13108c2ecf20Sopenharmony_ci		dev_dbg(&state->priv->i2c->dev, "%s() demod%d DVB-S\n",
13118c2ecf20Sopenharmony_ci			__func__, state->demod);
13128c2ecf20Sopenharmony_ci
13138c2ecf20Sopenharmony_ci		/* Only QPSK is supported for DVB-S */
13148c2ecf20Sopenharmony_ci		if (c->modulation != QPSK) {
13158c2ecf20Sopenharmony_ci			dev_dbg(&state->priv->i2c->dev,
13168c2ecf20Sopenharmony_ci				"%s() demod%d unsupported modulation (%d)\n",
13178c2ecf20Sopenharmony_ci				__func__, state->demod, c->modulation);
13188c2ecf20Sopenharmony_ci			return -EINVAL;
13198c2ecf20Sopenharmony_ci		}
13208c2ecf20Sopenharmony_ci
13218c2ecf20Sopenharmony_ci		/* Pilot doesn't exist in DVB-S, turn bit off */
13228c2ecf20Sopenharmony_ci		state->dnxt.pilot_val = CX24117_PILOT_OFF;
13238c2ecf20Sopenharmony_ci
13248c2ecf20Sopenharmony_ci		/* DVB-S only supports 0.35 */
13258c2ecf20Sopenharmony_ci		state->dnxt.rolloff_val = CX24117_ROLLOFF_035;
13268c2ecf20Sopenharmony_ci		break;
13278c2ecf20Sopenharmony_ci
13288c2ecf20Sopenharmony_ci	case SYS_DVBS2:
13298c2ecf20Sopenharmony_ci		dev_dbg(&state->priv->i2c->dev, "%s() demod%d DVB-S2\n",
13308c2ecf20Sopenharmony_ci			__func__, state->demod);
13318c2ecf20Sopenharmony_ci
13328c2ecf20Sopenharmony_ci		/*
13338c2ecf20Sopenharmony_ci		 * NBC 8PSK/QPSK with DVB-S is supported for DVB-S2,
13348c2ecf20Sopenharmony_ci		 * but not hardware auto detection
13358c2ecf20Sopenharmony_ci		 */
13368c2ecf20Sopenharmony_ci		if (c->modulation != PSK_8 && c->modulation != QPSK) {
13378c2ecf20Sopenharmony_ci			dev_dbg(&state->priv->i2c->dev,
13388c2ecf20Sopenharmony_ci				"%s() demod%d unsupported modulation (%d)\n",
13398c2ecf20Sopenharmony_ci				__func__, state->demod, c->modulation);
13408c2ecf20Sopenharmony_ci			return -EOPNOTSUPP;
13418c2ecf20Sopenharmony_ci		}
13428c2ecf20Sopenharmony_ci
13438c2ecf20Sopenharmony_ci		switch (c->pilot) {
13448c2ecf20Sopenharmony_ci		case PILOT_AUTO:
13458c2ecf20Sopenharmony_ci			state->dnxt.pilot_val = CX24117_PILOT_AUTO;
13468c2ecf20Sopenharmony_ci			break;
13478c2ecf20Sopenharmony_ci		case PILOT_OFF:
13488c2ecf20Sopenharmony_ci			state->dnxt.pilot_val = CX24117_PILOT_OFF;
13498c2ecf20Sopenharmony_ci			break;
13508c2ecf20Sopenharmony_ci		case PILOT_ON:
13518c2ecf20Sopenharmony_ci			state->dnxt.pilot_val = CX24117_PILOT_ON;
13528c2ecf20Sopenharmony_ci			break;
13538c2ecf20Sopenharmony_ci		default:
13548c2ecf20Sopenharmony_ci			dev_dbg(&state->priv->i2c->dev,
13558c2ecf20Sopenharmony_ci				"%s() demod%d unsupported pilot mode (%d)\n",
13568c2ecf20Sopenharmony_ci				__func__, state->demod, c->pilot);
13578c2ecf20Sopenharmony_ci			return -EOPNOTSUPP;
13588c2ecf20Sopenharmony_ci		}
13598c2ecf20Sopenharmony_ci
13608c2ecf20Sopenharmony_ci		switch (c->rolloff) {
13618c2ecf20Sopenharmony_ci		case ROLLOFF_20:
13628c2ecf20Sopenharmony_ci			state->dnxt.rolloff_val = CX24117_ROLLOFF_020;
13638c2ecf20Sopenharmony_ci			break;
13648c2ecf20Sopenharmony_ci		case ROLLOFF_25:
13658c2ecf20Sopenharmony_ci			state->dnxt.rolloff_val = CX24117_ROLLOFF_025;
13668c2ecf20Sopenharmony_ci			break;
13678c2ecf20Sopenharmony_ci		case ROLLOFF_35:
13688c2ecf20Sopenharmony_ci			state->dnxt.rolloff_val = CX24117_ROLLOFF_035;
13698c2ecf20Sopenharmony_ci			break;
13708c2ecf20Sopenharmony_ci		case ROLLOFF_AUTO:
13718c2ecf20Sopenharmony_ci			state->dnxt.rolloff_val = CX24117_ROLLOFF_035;
13728c2ecf20Sopenharmony_ci			/* soft-auto rolloff */
13738c2ecf20Sopenharmony_ci			retune = 3;
13748c2ecf20Sopenharmony_ci			break;
13758c2ecf20Sopenharmony_ci		default:
13768c2ecf20Sopenharmony_ci			dev_warn(&state->priv->i2c->dev,
13778c2ecf20Sopenharmony_ci				"%s: demod%d unsupported rolloff (%d)\n",
13788c2ecf20Sopenharmony_ci				KBUILD_MODNAME, state->demod, c->rolloff);
13798c2ecf20Sopenharmony_ci			return -EOPNOTSUPP;
13808c2ecf20Sopenharmony_ci		}
13818c2ecf20Sopenharmony_ci		break;
13828c2ecf20Sopenharmony_ci
13838c2ecf20Sopenharmony_ci	default:
13848c2ecf20Sopenharmony_ci		dev_warn(&state->priv->i2c->dev,
13858c2ecf20Sopenharmony_ci			"%s: demod %d unsupported delivery system (%d)\n",
13868c2ecf20Sopenharmony_ci			KBUILD_MODNAME, state->demod, c->delivery_system);
13878c2ecf20Sopenharmony_ci		return -EINVAL;
13888c2ecf20Sopenharmony_ci	}
13898c2ecf20Sopenharmony_ci
13908c2ecf20Sopenharmony_ci	state->dnxt.delsys = c->delivery_system;
13918c2ecf20Sopenharmony_ci	state->dnxt.modulation = c->modulation;
13928c2ecf20Sopenharmony_ci	state->dnxt.frequency = c->frequency;
13938c2ecf20Sopenharmony_ci	state->dnxt.pilot = c->pilot;
13948c2ecf20Sopenharmony_ci	state->dnxt.rolloff = c->rolloff;
13958c2ecf20Sopenharmony_ci
13968c2ecf20Sopenharmony_ci	ret = cx24117_set_inversion(state, c->inversion);
13978c2ecf20Sopenharmony_ci	if (ret !=  0)
13988c2ecf20Sopenharmony_ci		return ret;
13998c2ecf20Sopenharmony_ci
14008c2ecf20Sopenharmony_ci	ret = cx24117_set_fec(state,
14018c2ecf20Sopenharmony_ci		c->delivery_system, c->modulation, c->fec_inner);
14028c2ecf20Sopenharmony_ci	if (ret !=  0)
14038c2ecf20Sopenharmony_ci		return ret;
14048c2ecf20Sopenharmony_ci
14058c2ecf20Sopenharmony_ci	ret = cx24117_set_symbolrate(state, c->symbol_rate);
14068c2ecf20Sopenharmony_ci	if (ret !=  0)
14078c2ecf20Sopenharmony_ci		return ret;
14088c2ecf20Sopenharmony_ci
14098c2ecf20Sopenharmony_ci	/* discard the 'current' tuning parameters and prepare to tune */
14108c2ecf20Sopenharmony_ci	cx24117_clone_params(fe);
14118c2ecf20Sopenharmony_ci
14128c2ecf20Sopenharmony_ci	dev_dbg(&state->priv->i2c->dev,
14138c2ecf20Sopenharmony_ci		"%s: delsys      = %d\n", __func__, state->dcur.delsys);
14148c2ecf20Sopenharmony_ci	dev_dbg(&state->priv->i2c->dev,
14158c2ecf20Sopenharmony_ci		"%s: modulation  = %d\n", __func__, state->dcur.modulation);
14168c2ecf20Sopenharmony_ci	dev_dbg(&state->priv->i2c->dev,
14178c2ecf20Sopenharmony_ci		"%s: frequency   = %d\n", __func__, state->dcur.frequency);
14188c2ecf20Sopenharmony_ci	dev_dbg(&state->priv->i2c->dev,
14198c2ecf20Sopenharmony_ci		"%s: pilot       = %d (val = 0x%02x)\n", __func__,
14208c2ecf20Sopenharmony_ci		state->dcur.pilot, state->dcur.pilot_val);
14218c2ecf20Sopenharmony_ci	dev_dbg(&state->priv->i2c->dev,
14228c2ecf20Sopenharmony_ci		"%s: retune      = %d\n", __func__, retune);
14238c2ecf20Sopenharmony_ci	dev_dbg(&state->priv->i2c->dev,
14248c2ecf20Sopenharmony_ci		"%s: rolloff     = %d (val = 0x%02x)\n", __func__,
14258c2ecf20Sopenharmony_ci		state->dcur.rolloff, state->dcur.rolloff_val);
14268c2ecf20Sopenharmony_ci	dev_dbg(&state->priv->i2c->dev,
14278c2ecf20Sopenharmony_ci		"%s: symbol_rate = %d\n", __func__, state->dcur.symbol_rate);
14288c2ecf20Sopenharmony_ci	dev_dbg(&state->priv->i2c->dev,
14298c2ecf20Sopenharmony_ci		"%s: FEC         = %d (mask/val = 0x%02x/0x%02x)\n", __func__,
14308c2ecf20Sopenharmony_ci		state->dcur.fec, state->dcur.fec_mask, state->dcur.fec_val);
14318c2ecf20Sopenharmony_ci	dev_dbg(&state->priv->i2c->dev,
14328c2ecf20Sopenharmony_ci		"%s: Inversion   = %d (val = 0x%02x)\n", __func__,
14338c2ecf20Sopenharmony_ci		state->dcur.inversion, state->dcur.inversion_val);
14348c2ecf20Sopenharmony_ci
14358c2ecf20Sopenharmony_ci	/* Prepare a tune request */
14368c2ecf20Sopenharmony_ci	cmd.args[0] = CMD_TUNEREQUEST;
14378c2ecf20Sopenharmony_ci
14388c2ecf20Sopenharmony_ci	/* demod */
14398c2ecf20Sopenharmony_ci	cmd.args[1] = state->demod;
14408c2ecf20Sopenharmony_ci
14418c2ecf20Sopenharmony_ci	/* Frequency */
14428c2ecf20Sopenharmony_ci	cmd.args[2] = (state->dcur.frequency & 0xff0000) >> 16;
14438c2ecf20Sopenharmony_ci	cmd.args[3] = (state->dcur.frequency & 0x00ff00) >> 8;
14448c2ecf20Sopenharmony_ci	cmd.args[4] = (state->dcur.frequency & 0x0000ff);
14458c2ecf20Sopenharmony_ci
14468c2ecf20Sopenharmony_ci	/* Symbol Rate */
14478c2ecf20Sopenharmony_ci	cmd.args[5] = ((state->dcur.symbol_rate / 1000) & 0xff00) >> 8;
14488c2ecf20Sopenharmony_ci	cmd.args[6] = ((state->dcur.symbol_rate / 1000) & 0x00ff);
14498c2ecf20Sopenharmony_ci
14508c2ecf20Sopenharmony_ci	/* Automatic Inversion */
14518c2ecf20Sopenharmony_ci	cmd.args[7] = state->dcur.inversion_val;
14528c2ecf20Sopenharmony_ci
14538c2ecf20Sopenharmony_ci	/* Modulation / FEC / Pilot */
14548c2ecf20Sopenharmony_ci	cmd.args[8] = state->dcur.fec_val | state->dcur.pilot_val;
14558c2ecf20Sopenharmony_ci
14568c2ecf20Sopenharmony_ci	cmd.args[9] = CX24117_SEARCH_RANGE_KHZ >> 8;
14578c2ecf20Sopenharmony_ci	cmd.args[10] = CX24117_SEARCH_RANGE_KHZ & 0xff;
14588c2ecf20Sopenharmony_ci
14598c2ecf20Sopenharmony_ci	cmd.args[11] = state->dcur.rolloff_val;
14608c2ecf20Sopenharmony_ci	cmd.args[12] = state->dcur.fec_mask;
14618c2ecf20Sopenharmony_ci
14628c2ecf20Sopenharmony_ci	if (state->dcur.symbol_rate > 30000000) {
14638c2ecf20Sopenharmony_ci		reg_ratediv = 0x04;
14648c2ecf20Sopenharmony_ci		reg_clkdiv = 0x02;
14658c2ecf20Sopenharmony_ci	} else if (state->dcur.symbol_rate > 10000000) {
14668c2ecf20Sopenharmony_ci		reg_ratediv = 0x06;
14678c2ecf20Sopenharmony_ci		reg_clkdiv = 0x03;
14688c2ecf20Sopenharmony_ci	} else {
14698c2ecf20Sopenharmony_ci		reg_ratediv = 0x0a;
14708c2ecf20Sopenharmony_ci		reg_clkdiv = 0x05;
14718c2ecf20Sopenharmony_ci	}
14728c2ecf20Sopenharmony_ci
14738c2ecf20Sopenharmony_ci	cmd.args[13] = reg_ratediv;
14748c2ecf20Sopenharmony_ci	cmd.args[14] = reg_clkdiv;
14758c2ecf20Sopenharmony_ci
14768c2ecf20Sopenharmony_ci	cx24117_writereg(state, (state->demod == 0) ?
14778c2ecf20Sopenharmony_ci		CX24117_REG_CLKDIV0 : CX24117_REG_CLKDIV1, reg_clkdiv);
14788c2ecf20Sopenharmony_ci	cx24117_writereg(state, (state->demod == 0) ?
14798c2ecf20Sopenharmony_ci		CX24117_REG_RATEDIV0 : CX24117_REG_RATEDIV1, reg_ratediv);
14808c2ecf20Sopenharmony_ci
14818c2ecf20Sopenharmony_ci	cmd.args[15] = CX24117_PNE;
14828c2ecf20Sopenharmony_ci	cmd.len = 16;
14838c2ecf20Sopenharmony_ci
14848c2ecf20Sopenharmony_ci	do {
14858c2ecf20Sopenharmony_ci		/* Reset status register */
14868c2ecf20Sopenharmony_ci		status = cx24117_readreg(state, (state->demod == 0) ?
14878c2ecf20Sopenharmony_ci			CX24117_REG_SSTATUS0 : CX24117_REG_SSTATUS1) &
14888c2ecf20Sopenharmony_ci			CX24117_SIGNAL_MASK;
14898c2ecf20Sopenharmony_ci
14908c2ecf20Sopenharmony_ci		dev_dbg(&state->priv->i2c->dev,
14918c2ecf20Sopenharmony_ci			"%s() demod%d status_setfe = %02x\n",
14928c2ecf20Sopenharmony_ci			__func__, state->demod, status);
14938c2ecf20Sopenharmony_ci
14948c2ecf20Sopenharmony_ci		cx24117_writereg(state, (state->demod == 0) ?
14958c2ecf20Sopenharmony_ci			CX24117_REG_SSTATUS0 : CX24117_REG_SSTATUS1, status);
14968c2ecf20Sopenharmony_ci
14978c2ecf20Sopenharmony_ci		/* Tune */
14988c2ecf20Sopenharmony_ci		ret = cx24117_cmd_execute(fe, &cmd);
14998c2ecf20Sopenharmony_ci		if (ret != 0)
15008c2ecf20Sopenharmony_ci			break;
15018c2ecf20Sopenharmony_ci
15028c2ecf20Sopenharmony_ci		/*
15038c2ecf20Sopenharmony_ci		 * Wait for up to 500 ms before retrying
15048c2ecf20Sopenharmony_ci		 *
15058c2ecf20Sopenharmony_ci		 * If we are able to tune then generally it occurs within 100ms.
15068c2ecf20Sopenharmony_ci		 * If it takes longer, try a different rolloff setting.
15078c2ecf20Sopenharmony_ci		 */
15088c2ecf20Sopenharmony_ci		for (i = 0; i < 50; i++) {
15098c2ecf20Sopenharmony_ci			cx24117_read_status(fe, &tunerstat);
15108c2ecf20Sopenharmony_ci			status = tunerstat & (FE_HAS_SIGNAL | FE_HAS_SYNC);
15118c2ecf20Sopenharmony_ci			if (status == (FE_HAS_SIGNAL | FE_HAS_SYNC)) {
15128c2ecf20Sopenharmony_ci				dev_dbg(&state->priv->i2c->dev,
15138c2ecf20Sopenharmony_ci					"%s() demod%d tuned\n",
15148c2ecf20Sopenharmony_ci					__func__, state->demod);
15158c2ecf20Sopenharmony_ci				return 0;
15168c2ecf20Sopenharmony_ci			}
15178c2ecf20Sopenharmony_ci			msleep(20);
15188c2ecf20Sopenharmony_ci		}
15198c2ecf20Sopenharmony_ci
15208c2ecf20Sopenharmony_ci		dev_dbg(&state->priv->i2c->dev, "%s() demod%d not tuned\n",
15218c2ecf20Sopenharmony_ci			__func__, state->demod);
15228c2ecf20Sopenharmony_ci
15238c2ecf20Sopenharmony_ci		/* try next rolloff value */
15248c2ecf20Sopenharmony_ci		if (state->dcur.rolloff == 3)
15258c2ecf20Sopenharmony_ci			cmd.args[11]--;
15268c2ecf20Sopenharmony_ci
15278c2ecf20Sopenharmony_ci	} while (--retune);
15288c2ecf20Sopenharmony_ci	return -EINVAL;
15298c2ecf20Sopenharmony_ci}
15308c2ecf20Sopenharmony_ci
15318c2ecf20Sopenharmony_cistatic int cx24117_tune(struct dvb_frontend *fe, bool re_tune,
15328c2ecf20Sopenharmony_ci	unsigned int mode_flags, unsigned int *delay, enum fe_status *status)
15338c2ecf20Sopenharmony_ci{
15348c2ecf20Sopenharmony_ci	struct cx24117_state *state = fe->demodulator_priv;
15358c2ecf20Sopenharmony_ci
15368c2ecf20Sopenharmony_ci	dev_dbg(&state->priv->i2c->dev, "%s() demod%d\n",
15378c2ecf20Sopenharmony_ci		__func__, state->demod);
15388c2ecf20Sopenharmony_ci
15398c2ecf20Sopenharmony_ci	*delay = HZ / 5;
15408c2ecf20Sopenharmony_ci	if (re_tune) {
15418c2ecf20Sopenharmony_ci		int ret = cx24117_set_frontend(fe);
15428c2ecf20Sopenharmony_ci		if (ret)
15438c2ecf20Sopenharmony_ci			return ret;
15448c2ecf20Sopenharmony_ci	}
15458c2ecf20Sopenharmony_ci	return cx24117_read_status(fe, status);
15468c2ecf20Sopenharmony_ci}
15478c2ecf20Sopenharmony_ci
15488c2ecf20Sopenharmony_cistatic enum dvbfe_algo cx24117_get_algo(struct dvb_frontend *fe)
15498c2ecf20Sopenharmony_ci{
15508c2ecf20Sopenharmony_ci	return DVBFE_ALGO_HW;
15518c2ecf20Sopenharmony_ci}
15528c2ecf20Sopenharmony_ci
15538c2ecf20Sopenharmony_cistatic int cx24117_get_frontend(struct dvb_frontend *fe,
15548c2ecf20Sopenharmony_ci				struct dtv_frontend_properties *c)
15558c2ecf20Sopenharmony_ci{
15568c2ecf20Sopenharmony_ci	struct cx24117_state *state = fe->demodulator_priv;
15578c2ecf20Sopenharmony_ci	struct cx24117_cmd cmd;
15588c2ecf20Sopenharmony_ci	u8 reg, st, inv;
15598c2ecf20Sopenharmony_ci	int ret, idx;
15608c2ecf20Sopenharmony_ci	unsigned int freq;
15618c2ecf20Sopenharmony_ci	short srate_os, freq_os;
15628c2ecf20Sopenharmony_ci
15638c2ecf20Sopenharmony_ci	u8 buf[0x1f-4];
15648c2ecf20Sopenharmony_ci
15658c2ecf20Sopenharmony_ci	/* Read current tune parameters */
15668c2ecf20Sopenharmony_ci	cmd.args[0] = CMD_GETCTLACC;
15678c2ecf20Sopenharmony_ci	cmd.args[1] = (u8) state->demod;
15688c2ecf20Sopenharmony_ci	cmd.len = 2;
15698c2ecf20Sopenharmony_ci	ret = cx24117_cmd_execute(fe, &cmd);
15708c2ecf20Sopenharmony_ci	if (ret != 0)
15718c2ecf20Sopenharmony_ci		return ret;
15728c2ecf20Sopenharmony_ci
15738c2ecf20Sopenharmony_ci	/* read all required regs at once */
15748c2ecf20Sopenharmony_ci	reg = (state->demod == 0) ? CX24117_REG_FREQ3_0 : CX24117_REG_FREQ3_1;
15758c2ecf20Sopenharmony_ci	ret = cx24117_readregN(state, reg, buf, 0x1f-4);
15768c2ecf20Sopenharmony_ci	if (ret != 0)
15778c2ecf20Sopenharmony_ci		return ret;
15788c2ecf20Sopenharmony_ci
15798c2ecf20Sopenharmony_ci	st = buf[5];
15808c2ecf20Sopenharmony_ci
15818c2ecf20Sopenharmony_ci	/* get spectral inversion */
15828c2ecf20Sopenharmony_ci	inv = (((state->demod == 0) ? ~st : st) >> 6) & 1;
15838c2ecf20Sopenharmony_ci	if (inv == 0)
15848c2ecf20Sopenharmony_ci		c->inversion = INVERSION_OFF;
15858c2ecf20Sopenharmony_ci	else
15868c2ecf20Sopenharmony_ci		c->inversion = INVERSION_ON;
15878c2ecf20Sopenharmony_ci
15888c2ecf20Sopenharmony_ci	/* modulation and fec */
15898c2ecf20Sopenharmony_ci	idx = st & 0x3f;
15908c2ecf20Sopenharmony_ci	if (c->delivery_system == SYS_DVBS2) {
15918c2ecf20Sopenharmony_ci		if (idx > 11)
15928c2ecf20Sopenharmony_ci			idx += 9;
15938c2ecf20Sopenharmony_ci		else
15948c2ecf20Sopenharmony_ci			idx += 7;
15958c2ecf20Sopenharmony_ci	}
15968c2ecf20Sopenharmony_ci
15978c2ecf20Sopenharmony_ci	c->modulation = cx24117_modfec_modes[idx].modulation;
15988c2ecf20Sopenharmony_ci	c->fec_inner = cx24117_modfec_modes[idx].fec;
15998c2ecf20Sopenharmony_ci
16008c2ecf20Sopenharmony_ci	/* frequency */
16018c2ecf20Sopenharmony_ci	freq = (buf[0] << 16) | (buf[1] << 8) | buf[2];
16028c2ecf20Sopenharmony_ci	freq_os = (buf[8] << 8) | buf[9];
16038c2ecf20Sopenharmony_ci	c->frequency = freq + freq_os;
16048c2ecf20Sopenharmony_ci
16058c2ecf20Sopenharmony_ci	/* symbol rate */
16068c2ecf20Sopenharmony_ci	srate_os = (buf[10] << 8) | buf[11];
16078c2ecf20Sopenharmony_ci	c->symbol_rate = -1000 * srate_os + state->dcur.symbol_rate;
16088c2ecf20Sopenharmony_ci	return 0;
16098c2ecf20Sopenharmony_ci}
16108c2ecf20Sopenharmony_ci
16118c2ecf20Sopenharmony_cistatic const struct dvb_frontend_ops cx24117_ops = {
16128c2ecf20Sopenharmony_ci	.delsys = { SYS_DVBS, SYS_DVBS2 },
16138c2ecf20Sopenharmony_ci	.info = {
16148c2ecf20Sopenharmony_ci		.name = "Conexant CX24117/CX24132",
16158c2ecf20Sopenharmony_ci		.frequency_min_hz =  950 * MHz,
16168c2ecf20Sopenharmony_ci		.frequency_max_hz = 2150 * MHz,
16178c2ecf20Sopenharmony_ci		.frequency_stepsize_hz = 1011 * kHz,
16188c2ecf20Sopenharmony_ci		.frequency_tolerance_hz = 5 * MHz,
16198c2ecf20Sopenharmony_ci		.symbol_rate_min = 1000000,
16208c2ecf20Sopenharmony_ci		.symbol_rate_max = 45000000,
16218c2ecf20Sopenharmony_ci		.caps = FE_CAN_INVERSION_AUTO |
16228c2ecf20Sopenharmony_ci			FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | FE_CAN_FEC_3_4 |
16238c2ecf20Sopenharmony_ci			FE_CAN_FEC_4_5 | FE_CAN_FEC_5_6 | FE_CAN_FEC_6_7 |
16248c2ecf20Sopenharmony_ci			FE_CAN_FEC_7_8 | FE_CAN_FEC_AUTO |
16258c2ecf20Sopenharmony_ci			FE_CAN_2G_MODULATION |
16268c2ecf20Sopenharmony_ci			FE_CAN_QPSK | FE_CAN_RECOVER
16278c2ecf20Sopenharmony_ci	},
16288c2ecf20Sopenharmony_ci
16298c2ecf20Sopenharmony_ci	.release = cx24117_release,
16308c2ecf20Sopenharmony_ci
16318c2ecf20Sopenharmony_ci	.init = cx24117_initfe,
16328c2ecf20Sopenharmony_ci	.sleep = cx24117_sleep,
16338c2ecf20Sopenharmony_ci	.read_status = cx24117_read_status,
16348c2ecf20Sopenharmony_ci	.read_ber = cx24117_read_ber,
16358c2ecf20Sopenharmony_ci	.read_signal_strength = cx24117_read_signal_strength,
16368c2ecf20Sopenharmony_ci	.read_snr = cx24117_read_snr,
16378c2ecf20Sopenharmony_ci	.read_ucblocks = cx24117_read_ucblocks,
16388c2ecf20Sopenharmony_ci	.set_tone = cx24117_set_tone,
16398c2ecf20Sopenharmony_ci	.set_voltage = cx24117_set_voltage,
16408c2ecf20Sopenharmony_ci	.diseqc_send_master_cmd = cx24117_send_diseqc_msg,
16418c2ecf20Sopenharmony_ci	.diseqc_send_burst = cx24117_diseqc_send_burst,
16428c2ecf20Sopenharmony_ci	.get_frontend_algo = cx24117_get_algo,
16438c2ecf20Sopenharmony_ci	.tune = cx24117_tune,
16448c2ecf20Sopenharmony_ci
16458c2ecf20Sopenharmony_ci	.set_frontend = cx24117_set_frontend,
16468c2ecf20Sopenharmony_ci	.get_frontend = cx24117_get_frontend,
16478c2ecf20Sopenharmony_ci};
16488c2ecf20Sopenharmony_ci
16498c2ecf20Sopenharmony_ci
16508c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("DVB Frontend module for Conexant cx24117/cx24132 hardware");
16518c2ecf20Sopenharmony_ciMODULE_AUTHOR("Luis Alves (ljalvs@gmail.com)");
16528c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL");
16538c2ecf20Sopenharmony_ciMODULE_VERSION("1.1");
16548c2ecf20Sopenharmony_ciMODULE_FIRMWARE(CX24117_DEFAULT_FIRMWARE);
16558c2ecf20Sopenharmony_ci
1656