18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci    Montage Technology DS3000 - DVBS/S2 Demodulator driver
48c2ecf20Sopenharmony_ci    Copyright (C) 2009-2012 Konstantin Dimitrov <kosio.dimitrov@gmail.com>
58c2ecf20Sopenharmony_ci
68c2ecf20Sopenharmony_ci    Copyright (C) 2009-2012 TurboSight.com
78c2ecf20Sopenharmony_ci
88c2ecf20Sopenharmony_ci */
98c2ecf20Sopenharmony_ci
108c2ecf20Sopenharmony_ci#include <linux/slab.h>
118c2ecf20Sopenharmony_ci#include <linux/kernel.h>
128c2ecf20Sopenharmony_ci#include <linux/module.h>
138c2ecf20Sopenharmony_ci#include <linux/moduleparam.h>
148c2ecf20Sopenharmony_ci#include <linux/init.h>
158c2ecf20Sopenharmony_ci#include <linux/firmware.h>
168c2ecf20Sopenharmony_ci
178c2ecf20Sopenharmony_ci#include <media/dvb_frontend.h>
188c2ecf20Sopenharmony_ci#include "ts2020.h"
198c2ecf20Sopenharmony_ci#include "ds3000.h"
208c2ecf20Sopenharmony_ci
218c2ecf20Sopenharmony_cistatic int debug;
228c2ecf20Sopenharmony_ci
238c2ecf20Sopenharmony_ci#define dprintk(args...) \
248c2ecf20Sopenharmony_ci	do { \
258c2ecf20Sopenharmony_ci		if (debug) \
268c2ecf20Sopenharmony_ci			printk(args); \
278c2ecf20Sopenharmony_ci	} while (0)
288c2ecf20Sopenharmony_ci
298c2ecf20Sopenharmony_ci/* as of March 2009 current DS3000 firmware version is 1.78 */
308c2ecf20Sopenharmony_ci/* DS3000 FW v1.78 MD5: a32d17910c4f370073f9346e71d34b80 */
318c2ecf20Sopenharmony_ci#define DS3000_DEFAULT_FIRMWARE "dvb-fe-ds3000.fw"
328c2ecf20Sopenharmony_ci
338c2ecf20Sopenharmony_ci#define DS3000_SAMPLE_RATE 96000 /* in kHz */
348c2ecf20Sopenharmony_ci
358c2ecf20Sopenharmony_ci/* Register values to initialise the demod in DVB-S mode */
368c2ecf20Sopenharmony_cistatic u8 ds3000_dvbs_init_tab[] = {
378c2ecf20Sopenharmony_ci	0x23, 0x05,
388c2ecf20Sopenharmony_ci	0x08, 0x03,
398c2ecf20Sopenharmony_ci	0x0c, 0x00,
408c2ecf20Sopenharmony_ci	0x21, 0x54,
418c2ecf20Sopenharmony_ci	0x25, 0x82,
428c2ecf20Sopenharmony_ci	0x27, 0x31,
438c2ecf20Sopenharmony_ci	0x30, 0x08,
448c2ecf20Sopenharmony_ci	0x31, 0x40,
458c2ecf20Sopenharmony_ci	0x32, 0x32,
468c2ecf20Sopenharmony_ci	0x33, 0x35,
478c2ecf20Sopenharmony_ci	0x35, 0xff,
488c2ecf20Sopenharmony_ci	0x3a, 0x00,
498c2ecf20Sopenharmony_ci	0x37, 0x10,
508c2ecf20Sopenharmony_ci	0x38, 0x10,
518c2ecf20Sopenharmony_ci	0x39, 0x02,
528c2ecf20Sopenharmony_ci	0x42, 0x60,
538c2ecf20Sopenharmony_ci	0x4a, 0x40,
548c2ecf20Sopenharmony_ci	0x4b, 0x04,
558c2ecf20Sopenharmony_ci	0x4d, 0x91,
568c2ecf20Sopenharmony_ci	0x5d, 0xc8,
578c2ecf20Sopenharmony_ci	0x50, 0x77,
588c2ecf20Sopenharmony_ci	0x51, 0x77,
598c2ecf20Sopenharmony_ci	0x52, 0x36,
608c2ecf20Sopenharmony_ci	0x53, 0x36,
618c2ecf20Sopenharmony_ci	0x56, 0x01,
628c2ecf20Sopenharmony_ci	0x63, 0x43,
638c2ecf20Sopenharmony_ci	0x64, 0x30,
648c2ecf20Sopenharmony_ci	0x65, 0x40,
658c2ecf20Sopenharmony_ci	0x68, 0x26,
668c2ecf20Sopenharmony_ci	0x69, 0x4c,
678c2ecf20Sopenharmony_ci	0x70, 0x20,
688c2ecf20Sopenharmony_ci	0x71, 0x70,
698c2ecf20Sopenharmony_ci	0x72, 0x04,
708c2ecf20Sopenharmony_ci	0x73, 0x00,
718c2ecf20Sopenharmony_ci	0x70, 0x40,
728c2ecf20Sopenharmony_ci	0x71, 0x70,
738c2ecf20Sopenharmony_ci	0x72, 0x04,
748c2ecf20Sopenharmony_ci	0x73, 0x00,
758c2ecf20Sopenharmony_ci	0x70, 0x60,
768c2ecf20Sopenharmony_ci	0x71, 0x70,
778c2ecf20Sopenharmony_ci	0x72, 0x04,
788c2ecf20Sopenharmony_ci	0x73, 0x00,
798c2ecf20Sopenharmony_ci	0x70, 0x80,
808c2ecf20Sopenharmony_ci	0x71, 0x70,
818c2ecf20Sopenharmony_ci	0x72, 0x04,
828c2ecf20Sopenharmony_ci	0x73, 0x00,
838c2ecf20Sopenharmony_ci	0x70, 0xa0,
848c2ecf20Sopenharmony_ci	0x71, 0x70,
858c2ecf20Sopenharmony_ci	0x72, 0x04,
868c2ecf20Sopenharmony_ci	0x73, 0x00,
878c2ecf20Sopenharmony_ci	0x70, 0x1f,
888c2ecf20Sopenharmony_ci	0x76, 0x00,
898c2ecf20Sopenharmony_ci	0x77, 0xd1,
908c2ecf20Sopenharmony_ci	0x78, 0x0c,
918c2ecf20Sopenharmony_ci	0x79, 0x80,
928c2ecf20Sopenharmony_ci	0x7f, 0x04,
938c2ecf20Sopenharmony_ci	0x7c, 0x00,
948c2ecf20Sopenharmony_ci	0x80, 0x86,
958c2ecf20Sopenharmony_ci	0x81, 0xa6,
968c2ecf20Sopenharmony_ci	0x85, 0x04,
978c2ecf20Sopenharmony_ci	0xcd, 0xf4,
988c2ecf20Sopenharmony_ci	0x90, 0x33,
998c2ecf20Sopenharmony_ci	0xa0, 0x44,
1008c2ecf20Sopenharmony_ci	0xc0, 0x18,
1018c2ecf20Sopenharmony_ci	0xc3, 0x10,
1028c2ecf20Sopenharmony_ci	0xc4, 0x08,
1038c2ecf20Sopenharmony_ci	0xc5, 0x80,
1048c2ecf20Sopenharmony_ci	0xc6, 0x80,
1058c2ecf20Sopenharmony_ci	0xc7, 0x0a,
1068c2ecf20Sopenharmony_ci	0xc8, 0x1a,
1078c2ecf20Sopenharmony_ci	0xc9, 0x80,
1088c2ecf20Sopenharmony_ci	0xfe, 0x92,
1098c2ecf20Sopenharmony_ci	0xe0, 0xf8,
1108c2ecf20Sopenharmony_ci	0xe6, 0x8b,
1118c2ecf20Sopenharmony_ci	0xd0, 0x40,
1128c2ecf20Sopenharmony_ci	0xf8, 0x20,
1138c2ecf20Sopenharmony_ci	0xfa, 0x0f,
1148c2ecf20Sopenharmony_ci	0xfd, 0x20,
1158c2ecf20Sopenharmony_ci	0xad, 0x20,
1168c2ecf20Sopenharmony_ci	0xae, 0x07,
1178c2ecf20Sopenharmony_ci	0xb8, 0x00,
1188c2ecf20Sopenharmony_ci};
1198c2ecf20Sopenharmony_ci
1208c2ecf20Sopenharmony_ci/* Register values to initialise the demod in DVB-S2 mode */
1218c2ecf20Sopenharmony_cistatic u8 ds3000_dvbs2_init_tab[] = {
1228c2ecf20Sopenharmony_ci	0x23, 0x0f,
1238c2ecf20Sopenharmony_ci	0x08, 0x07,
1248c2ecf20Sopenharmony_ci	0x0c, 0x00,
1258c2ecf20Sopenharmony_ci	0x21, 0x54,
1268c2ecf20Sopenharmony_ci	0x25, 0x82,
1278c2ecf20Sopenharmony_ci	0x27, 0x31,
1288c2ecf20Sopenharmony_ci	0x30, 0x08,
1298c2ecf20Sopenharmony_ci	0x31, 0x32,
1308c2ecf20Sopenharmony_ci	0x32, 0x32,
1318c2ecf20Sopenharmony_ci	0x33, 0x35,
1328c2ecf20Sopenharmony_ci	0x35, 0xff,
1338c2ecf20Sopenharmony_ci	0x3a, 0x00,
1348c2ecf20Sopenharmony_ci	0x37, 0x10,
1358c2ecf20Sopenharmony_ci	0x38, 0x10,
1368c2ecf20Sopenharmony_ci	0x39, 0x02,
1378c2ecf20Sopenharmony_ci	0x42, 0x60,
1388c2ecf20Sopenharmony_ci	0x4a, 0x80,
1398c2ecf20Sopenharmony_ci	0x4b, 0x04,
1408c2ecf20Sopenharmony_ci	0x4d, 0x81,
1418c2ecf20Sopenharmony_ci	0x5d, 0x88,
1428c2ecf20Sopenharmony_ci	0x50, 0x36,
1438c2ecf20Sopenharmony_ci	0x51, 0x36,
1448c2ecf20Sopenharmony_ci	0x52, 0x36,
1458c2ecf20Sopenharmony_ci	0x53, 0x36,
1468c2ecf20Sopenharmony_ci	0x63, 0x60,
1478c2ecf20Sopenharmony_ci	0x64, 0x10,
1488c2ecf20Sopenharmony_ci	0x65, 0x10,
1498c2ecf20Sopenharmony_ci	0x68, 0x04,
1508c2ecf20Sopenharmony_ci	0x69, 0x29,
1518c2ecf20Sopenharmony_ci	0x70, 0x20,
1528c2ecf20Sopenharmony_ci	0x71, 0x70,
1538c2ecf20Sopenharmony_ci	0x72, 0x04,
1548c2ecf20Sopenharmony_ci	0x73, 0x00,
1558c2ecf20Sopenharmony_ci	0x70, 0x40,
1568c2ecf20Sopenharmony_ci	0x71, 0x70,
1578c2ecf20Sopenharmony_ci	0x72, 0x04,
1588c2ecf20Sopenharmony_ci	0x73, 0x00,
1598c2ecf20Sopenharmony_ci	0x70, 0x60,
1608c2ecf20Sopenharmony_ci	0x71, 0x70,
1618c2ecf20Sopenharmony_ci	0x72, 0x04,
1628c2ecf20Sopenharmony_ci	0x73, 0x00,
1638c2ecf20Sopenharmony_ci	0x70, 0x80,
1648c2ecf20Sopenharmony_ci	0x71, 0x70,
1658c2ecf20Sopenharmony_ci	0x72, 0x04,
1668c2ecf20Sopenharmony_ci	0x73, 0x00,
1678c2ecf20Sopenharmony_ci	0x70, 0xa0,
1688c2ecf20Sopenharmony_ci	0x71, 0x70,
1698c2ecf20Sopenharmony_ci	0x72, 0x04,
1708c2ecf20Sopenharmony_ci	0x73, 0x00,
1718c2ecf20Sopenharmony_ci	0x70, 0x1f,
1728c2ecf20Sopenharmony_ci	0xa0, 0x44,
1738c2ecf20Sopenharmony_ci	0xc0, 0x08,
1748c2ecf20Sopenharmony_ci	0xc1, 0x10,
1758c2ecf20Sopenharmony_ci	0xc2, 0x08,
1768c2ecf20Sopenharmony_ci	0xc3, 0x10,
1778c2ecf20Sopenharmony_ci	0xc4, 0x08,
1788c2ecf20Sopenharmony_ci	0xc5, 0xf0,
1798c2ecf20Sopenharmony_ci	0xc6, 0xf0,
1808c2ecf20Sopenharmony_ci	0xc7, 0x0a,
1818c2ecf20Sopenharmony_ci	0xc8, 0x1a,
1828c2ecf20Sopenharmony_ci	0xc9, 0x80,
1838c2ecf20Sopenharmony_ci	0xca, 0x23,
1848c2ecf20Sopenharmony_ci	0xcb, 0x24,
1858c2ecf20Sopenharmony_ci	0xce, 0x74,
1868c2ecf20Sopenharmony_ci	0x90, 0x03,
1878c2ecf20Sopenharmony_ci	0x76, 0x80,
1888c2ecf20Sopenharmony_ci	0x77, 0x42,
1898c2ecf20Sopenharmony_ci	0x78, 0x0a,
1908c2ecf20Sopenharmony_ci	0x79, 0x80,
1918c2ecf20Sopenharmony_ci	0xad, 0x40,
1928c2ecf20Sopenharmony_ci	0xae, 0x07,
1938c2ecf20Sopenharmony_ci	0x7f, 0xd4,
1948c2ecf20Sopenharmony_ci	0x7c, 0x00,
1958c2ecf20Sopenharmony_ci	0x80, 0xa8,
1968c2ecf20Sopenharmony_ci	0x81, 0xda,
1978c2ecf20Sopenharmony_ci	0x7c, 0x01,
1988c2ecf20Sopenharmony_ci	0x80, 0xda,
1998c2ecf20Sopenharmony_ci	0x81, 0xec,
2008c2ecf20Sopenharmony_ci	0x7c, 0x02,
2018c2ecf20Sopenharmony_ci	0x80, 0xca,
2028c2ecf20Sopenharmony_ci	0x81, 0xeb,
2038c2ecf20Sopenharmony_ci	0x7c, 0x03,
2048c2ecf20Sopenharmony_ci	0x80, 0xba,
2058c2ecf20Sopenharmony_ci	0x81, 0xdb,
2068c2ecf20Sopenharmony_ci	0x85, 0x08,
2078c2ecf20Sopenharmony_ci	0x86, 0x00,
2088c2ecf20Sopenharmony_ci	0x87, 0x02,
2098c2ecf20Sopenharmony_ci	0x89, 0x80,
2108c2ecf20Sopenharmony_ci	0x8b, 0x44,
2118c2ecf20Sopenharmony_ci	0x8c, 0xaa,
2128c2ecf20Sopenharmony_ci	0x8a, 0x10,
2138c2ecf20Sopenharmony_ci	0xba, 0x00,
2148c2ecf20Sopenharmony_ci	0xf5, 0x04,
2158c2ecf20Sopenharmony_ci	0xfe, 0x44,
2168c2ecf20Sopenharmony_ci	0xd2, 0x32,
2178c2ecf20Sopenharmony_ci	0xb8, 0x00,
2188c2ecf20Sopenharmony_ci};
2198c2ecf20Sopenharmony_ci
2208c2ecf20Sopenharmony_cistruct ds3000_state {
2218c2ecf20Sopenharmony_ci	struct i2c_adapter *i2c;
2228c2ecf20Sopenharmony_ci	const struct ds3000_config *config;
2238c2ecf20Sopenharmony_ci	struct dvb_frontend frontend;
2248c2ecf20Sopenharmony_ci	/* previous uncorrected block counter for DVB-S2 */
2258c2ecf20Sopenharmony_ci	u16 prevUCBS2;
2268c2ecf20Sopenharmony_ci};
2278c2ecf20Sopenharmony_ci
2288c2ecf20Sopenharmony_cistatic int ds3000_writereg(struct ds3000_state *state, int reg, int data)
2298c2ecf20Sopenharmony_ci{
2308c2ecf20Sopenharmony_ci	u8 buf[] = { reg, data };
2318c2ecf20Sopenharmony_ci	struct i2c_msg msg = { .addr = state->config->demod_address,
2328c2ecf20Sopenharmony_ci		.flags = 0, .buf = buf, .len = 2 };
2338c2ecf20Sopenharmony_ci	int err;
2348c2ecf20Sopenharmony_ci
2358c2ecf20Sopenharmony_ci	dprintk("%s: write reg 0x%02x, value 0x%02x\n", __func__, reg, data);
2368c2ecf20Sopenharmony_ci
2378c2ecf20Sopenharmony_ci	err = i2c_transfer(state->i2c, &msg, 1);
2388c2ecf20Sopenharmony_ci	if (err != 1) {
2398c2ecf20Sopenharmony_ci		printk(KERN_ERR "%s: writereg error(err == %i, reg == 0x%02x, value == 0x%02x)\n",
2408c2ecf20Sopenharmony_ci		       __func__, err, reg, data);
2418c2ecf20Sopenharmony_ci		return -EREMOTEIO;
2428c2ecf20Sopenharmony_ci	}
2438c2ecf20Sopenharmony_ci
2448c2ecf20Sopenharmony_ci	return 0;
2458c2ecf20Sopenharmony_ci}
2468c2ecf20Sopenharmony_ci
2478c2ecf20Sopenharmony_cistatic int ds3000_i2c_gate_ctrl(struct dvb_frontend *fe, int enable)
2488c2ecf20Sopenharmony_ci{
2498c2ecf20Sopenharmony_ci	struct ds3000_state *state = fe->demodulator_priv;
2508c2ecf20Sopenharmony_ci
2518c2ecf20Sopenharmony_ci	if (enable)
2528c2ecf20Sopenharmony_ci		ds3000_writereg(state, 0x03, 0x12);
2538c2ecf20Sopenharmony_ci	else
2548c2ecf20Sopenharmony_ci		ds3000_writereg(state, 0x03, 0x02);
2558c2ecf20Sopenharmony_ci
2568c2ecf20Sopenharmony_ci	return 0;
2578c2ecf20Sopenharmony_ci}
2588c2ecf20Sopenharmony_ci
2598c2ecf20Sopenharmony_ci/* I2C write for 8k firmware load */
2608c2ecf20Sopenharmony_cistatic int ds3000_writeFW(struct ds3000_state *state, int reg,
2618c2ecf20Sopenharmony_ci				const u8 *data, u16 len)
2628c2ecf20Sopenharmony_ci{
2638c2ecf20Sopenharmony_ci	int i, ret = 0;
2648c2ecf20Sopenharmony_ci	struct i2c_msg msg;
2658c2ecf20Sopenharmony_ci	u8 *buf;
2668c2ecf20Sopenharmony_ci
2678c2ecf20Sopenharmony_ci	buf = kmalloc(33, GFP_KERNEL);
2688c2ecf20Sopenharmony_ci	if (!buf)
2698c2ecf20Sopenharmony_ci		return -ENOMEM;
2708c2ecf20Sopenharmony_ci
2718c2ecf20Sopenharmony_ci	*(buf) = reg;
2728c2ecf20Sopenharmony_ci
2738c2ecf20Sopenharmony_ci	msg.addr = state->config->demod_address;
2748c2ecf20Sopenharmony_ci	msg.flags = 0;
2758c2ecf20Sopenharmony_ci	msg.buf = buf;
2768c2ecf20Sopenharmony_ci	msg.len = 33;
2778c2ecf20Sopenharmony_ci
2788c2ecf20Sopenharmony_ci	for (i = 0; i < len; i += 32) {
2798c2ecf20Sopenharmony_ci		memcpy(buf + 1, data + i, 32);
2808c2ecf20Sopenharmony_ci
2818c2ecf20Sopenharmony_ci		dprintk("%s: write reg 0x%02x, len = %d\n", __func__, reg, len);
2828c2ecf20Sopenharmony_ci
2838c2ecf20Sopenharmony_ci		ret = i2c_transfer(state->i2c, &msg, 1);
2848c2ecf20Sopenharmony_ci		if (ret != 1) {
2858c2ecf20Sopenharmony_ci			printk(KERN_ERR "%s: write error(err == %i, reg == 0x%02x\n",
2868c2ecf20Sopenharmony_ci			       __func__, ret, reg);
2878c2ecf20Sopenharmony_ci			ret = -EREMOTEIO;
2888c2ecf20Sopenharmony_ci			goto error;
2898c2ecf20Sopenharmony_ci		}
2908c2ecf20Sopenharmony_ci	}
2918c2ecf20Sopenharmony_ci	ret = 0;
2928c2ecf20Sopenharmony_ci
2938c2ecf20Sopenharmony_cierror:
2948c2ecf20Sopenharmony_ci	kfree(buf);
2958c2ecf20Sopenharmony_ci
2968c2ecf20Sopenharmony_ci	return ret;
2978c2ecf20Sopenharmony_ci}
2988c2ecf20Sopenharmony_ci
2998c2ecf20Sopenharmony_cistatic int ds3000_readreg(struct ds3000_state *state, u8 reg)
3008c2ecf20Sopenharmony_ci{
3018c2ecf20Sopenharmony_ci	int ret;
3028c2ecf20Sopenharmony_ci	u8 b0[] = { reg };
3038c2ecf20Sopenharmony_ci	u8 b1[] = { 0 };
3048c2ecf20Sopenharmony_ci	struct i2c_msg msg[] = {
3058c2ecf20Sopenharmony_ci		{
3068c2ecf20Sopenharmony_ci			.addr = state->config->demod_address,
3078c2ecf20Sopenharmony_ci			.flags = 0,
3088c2ecf20Sopenharmony_ci			.buf = b0,
3098c2ecf20Sopenharmony_ci			.len = 1
3108c2ecf20Sopenharmony_ci		}, {
3118c2ecf20Sopenharmony_ci			.addr = state->config->demod_address,
3128c2ecf20Sopenharmony_ci			.flags = I2C_M_RD,
3138c2ecf20Sopenharmony_ci			.buf = b1,
3148c2ecf20Sopenharmony_ci			.len = 1
3158c2ecf20Sopenharmony_ci		}
3168c2ecf20Sopenharmony_ci	};
3178c2ecf20Sopenharmony_ci
3188c2ecf20Sopenharmony_ci	ret = i2c_transfer(state->i2c, msg, 2);
3198c2ecf20Sopenharmony_ci
3208c2ecf20Sopenharmony_ci	if (ret != 2) {
3218c2ecf20Sopenharmony_ci		printk(KERN_ERR "%s: reg=0x%x(error=%d)\n", __func__, reg, ret);
3228c2ecf20Sopenharmony_ci		return ret;
3238c2ecf20Sopenharmony_ci	}
3248c2ecf20Sopenharmony_ci
3258c2ecf20Sopenharmony_ci	dprintk("%s: read reg 0x%02x, value 0x%02x\n", __func__, reg, b1[0]);
3268c2ecf20Sopenharmony_ci
3278c2ecf20Sopenharmony_ci	return b1[0];
3288c2ecf20Sopenharmony_ci}
3298c2ecf20Sopenharmony_ci
3308c2ecf20Sopenharmony_cistatic int ds3000_load_firmware(struct dvb_frontend *fe,
3318c2ecf20Sopenharmony_ci					const struct firmware *fw);
3328c2ecf20Sopenharmony_ci
3338c2ecf20Sopenharmony_cistatic int ds3000_firmware_ondemand(struct dvb_frontend *fe)
3348c2ecf20Sopenharmony_ci{
3358c2ecf20Sopenharmony_ci	struct ds3000_state *state = fe->demodulator_priv;
3368c2ecf20Sopenharmony_ci	const struct firmware *fw;
3378c2ecf20Sopenharmony_ci	int ret = 0;
3388c2ecf20Sopenharmony_ci
3398c2ecf20Sopenharmony_ci	dprintk("%s()\n", __func__);
3408c2ecf20Sopenharmony_ci
3418c2ecf20Sopenharmony_ci	ret = ds3000_readreg(state, 0xb2);
3428c2ecf20Sopenharmony_ci	if (ret < 0)
3438c2ecf20Sopenharmony_ci		return ret;
3448c2ecf20Sopenharmony_ci
3458c2ecf20Sopenharmony_ci	/* Load firmware */
3468c2ecf20Sopenharmony_ci	/* request the firmware, this will block until someone uploads it */
3478c2ecf20Sopenharmony_ci	printk(KERN_INFO "%s: Waiting for firmware upload (%s)...\n", __func__,
3488c2ecf20Sopenharmony_ci				DS3000_DEFAULT_FIRMWARE);
3498c2ecf20Sopenharmony_ci	ret = request_firmware(&fw, DS3000_DEFAULT_FIRMWARE,
3508c2ecf20Sopenharmony_ci				state->i2c->dev.parent);
3518c2ecf20Sopenharmony_ci	printk(KERN_INFO "%s: Waiting for firmware upload(2)...\n", __func__);
3528c2ecf20Sopenharmony_ci	if (ret) {
3538c2ecf20Sopenharmony_ci		printk(KERN_ERR "%s: No firmware uploaded (timeout or file not found?)\n",
3548c2ecf20Sopenharmony_ci		       __func__);
3558c2ecf20Sopenharmony_ci		return ret;
3568c2ecf20Sopenharmony_ci	}
3578c2ecf20Sopenharmony_ci
3588c2ecf20Sopenharmony_ci	ret = ds3000_load_firmware(fe, fw);
3598c2ecf20Sopenharmony_ci	if (ret)
3608c2ecf20Sopenharmony_ci		printk("%s: Writing firmware to device failed\n", __func__);
3618c2ecf20Sopenharmony_ci
3628c2ecf20Sopenharmony_ci	release_firmware(fw);
3638c2ecf20Sopenharmony_ci
3648c2ecf20Sopenharmony_ci	dprintk("%s: Firmware upload %s\n", __func__,
3658c2ecf20Sopenharmony_ci			ret == 0 ? "complete" : "failed");
3668c2ecf20Sopenharmony_ci
3678c2ecf20Sopenharmony_ci	return ret;
3688c2ecf20Sopenharmony_ci}
3698c2ecf20Sopenharmony_ci
3708c2ecf20Sopenharmony_cistatic int ds3000_load_firmware(struct dvb_frontend *fe,
3718c2ecf20Sopenharmony_ci					const struct firmware *fw)
3728c2ecf20Sopenharmony_ci{
3738c2ecf20Sopenharmony_ci	struct ds3000_state *state = fe->demodulator_priv;
3748c2ecf20Sopenharmony_ci	int ret = 0;
3758c2ecf20Sopenharmony_ci
3768c2ecf20Sopenharmony_ci	dprintk("%s\n", __func__);
3778c2ecf20Sopenharmony_ci	dprintk("Firmware is %zu bytes (%02x %02x .. %02x %02x)\n",
3788c2ecf20Sopenharmony_ci			fw->size,
3798c2ecf20Sopenharmony_ci			fw->data[0],
3808c2ecf20Sopenharmony_ci			fw->data[1],
3818c2ecf20Sopenharmony_ci			fw->data[fw->size - 2],
3828c2ecf20Sopenharmony_ci			fw->data[fw->size - 1]);
3838c2ecf20Sopenharmony_ci
3848c2ecf20Sopenharmony_ci	/* Begin the firmware load process */
3858c2ecf20Sopenharmony_ci	ds3000_writereg(state, 0xb2, 0x01);
3868c2ecf20Sopenharmony_ci	/* write the entire firmware */
3878c2ecf20Sopenharmony_ci	ret = ds3000_writeFW(state, 0xb0, fw->data, fw->size);
3888c2ecf20Sopenharmony_ci	ds3000_writereg(state, 0xb2, 0x00);
3898c2ecf20Sopenharmony_ci
3908c2ecf20Sopenharmony_ci	return ret;
3918c2ecf20Sopenharmony_ci}
3928c2ecf20Sopenharmony_ci
3938c2ecf20Sopenharmony_cistatic int ds3000_set_voltage(struct dvb_frontend *fe,
3948c2ecf20Sopenharmony_ci			      enum fe_sec_voltage voltage)
3958c2ecf20Sopenharmony_ci{
3968c2ecf20Sopenharmony_ci	struct ds3000_state *state = fe->demodulator_priv;
3978c2ecf20Sopenharmony_ci	u8 data;
3988c2ecf20Sopenharmony_ci
3998c2ecf20Sopenharmony_ci	dprintk("%s(%d)\n", __func__, voltage);
4008c2ecf20Sopenharmony_ci
4018c2ecf20Sopenharmony_ci	data = ds3000_readreg(state, 0xa2);
4028c2ecf20Sopenharmony_ci	data |= 0x03; /* bit0 V/H, bit1 off/on */
4038c2ecf20Sopenharmony_ci
4048c2ecf20Sopenharmony_ci	switch (voltage) {
4058c2ecf20Sopenharmony_ci	case SEC_VOLTAGE_18:
4068c2ecf20Sopenharmony_ci		data &= ~0x03;
4078c2ecf20Sopenharmony_ci		break;
4088c2ecf20Sopenharmony_ci	case SEC_VOLTAGE_13:
4098c2ecf20Sopenharmony_ci		data &= ~0x03;
4108c2ecf20Sopenharmony_ci		data |= 0x01;
4118c2ecf20Sopenharmony_ci		break;
4128c2ecf20Sopenharmony_ci	case SEC_VOLTAGE_OFF:
4138c2ecf20Sopenharmony_ci		break;
4148c2ecf20Sopenharmony_ci	}
4158c2ecf20Sopenharmony_ci
4168c2ecf20Sopenharmony_ci	ds3000_writereg(state, 0xa2, data);
4178c2ecf20Sopenharmony_ci
4188c2ecf20Sopenharmony_ci	return 0;
4198c2ecf20Sopenharmony_ci}
4208c2ecf20Sopenharmony_ci
4218c2ecf20Sopenharmony_cistatic int ds3000_read_status(struct dvb_frontend *fe, enum fe_status *status)
4228c2ecf20Sopenharmony_ci{
4238c2ecf20Sopenharmony_ci	struct ds3000_state *state = fe->demodulator_priv;
4248c2ecf20Sopenharmony_ci	struct dtv_frontend_properties *c = &fe->dtv_property_cache;
4258c2ecf20Sopenharmony_ci	int lock;
4268c2ecf20Sopenharmony_ci
4278c2ecf20Sopenharmony_ci	*status = 0;
4288c2ecf20Sopenharmony_ci
4298c2ecf20Sopenharmony_ci	switch (c->delivery_system) {
4308c2ecf20Sopenharmony_ci	case SYS_DVBS:
4318c2ecf20Sopenharmony_ci		lock = ds3000_readreg(state, 0xd1);
4328c2ecf20Sopenharmony_ci		if ((lock & 0x07) == 0x07)
4338c2ecf20Sopenharmony_ci			*status = FE_HAS_SIGNAL | FE_HAS_CARRIER |
4348c2ecf20Sopenharmony_ci				FE_HAS_VITERBI | FE_HAS_SYNC |
4358c2ecf20Sopenharmony_ci				FE_HAS_LOCK;
4368c2ecf20Sopenharmony_ci
4378c2ecf20Sopenharmony_ci		break;
4388c2ecf20Sopenharmony_ci	case SYS_DVBS2:
4398c2ecf20Sopenharmony_ci		lock = ds3000_readreg(state, 0x0d);
4408c2ecf20Sopenharmony_ci		if ((lock & 0x8f) == 0x8f)
4418c2ecf20Sopenharmony_ci			*status = FE_HAS_SIGNAL | FE_HAS_CARRIER |
4428c2ecf20Sopenharmony_ci				FE_HAS_VITERBI | FE_HAS_SYNC |
4438c2ecf20Sopenharmony_ci				FE_HAS_LOCK;
4448c2ecf20Sopenharmony_ci
4458c2ecf20Sopenharmony_ci		break;
4468c2ecf20Sopenharmony_ci	default:
4478c2ecf20Sopenharmony_ci		return -EINVAL;
4488c2ecf20Sopenharmony_ci	}
4498c2ecf20Sopenharmony_ci
4508c2ecf20Sopenharmony_ci	if (state->config->set_lock_led)
4518c2ecf20Sopenharmony_ci		state->config->set_lock_led(fe, *status == 0 ? 0 : 1);
4528c2ecf20Sopenharmony_ci
4538c2ecf20Sopenharmony_ci	dprintk("%s: status = 0x%02x\n", __func__, lock);
4548c2ecf20Sopenharmony_ci
4558c2ecf20Sopenharmony_ci	return 0;
4568c2ecf20Sopenharmony_ci}
4578c2ecf20Sopenharmony_ci
4588c2ecf20Sopenharmony_ci/* read DS3000 BER value */
4598c2ecf20Sopenharmony_cistatic int ds3000_read_ber(struct dvb_frontend *fe, u32* ber)
4608c2ecf20Sopenharmony_ci{
4618c2ecf20Sopenharmony_ci	struct ds3000_state *state = fe->demodulator_priv;
4628c2ecf20Sopenharmony_ci	struct dtv_frontend_properties *c = &fe->dtv_property_cache;
4638c2ecf20Sopenharmony_ci	u8 data;
4648c2ecf20Sopenharmony_ci	u32 ber_reading, lpdc_frames;
4658c2ecf20Sopenharmony_ci
4668c2ecf20Sopenharmony_ci	dprintk("%s()\n", __func__);
4678c2ecf20Sopenharmony_ci
4688c2ecf20Sopenharmony_ci	switch (c->delivery_system) {
4698c2ecf20Sopenharmony_ci	case SYS_DVBS:
4708c2ecf20Sopenharmony_ci		/* set the number of bytes checked during
4718c2ecf20Sopenharmony_ci		BER estimation */
4728c2ecf20Sopenharmony_ci		ds3000_writereg(state, 0xf9, 0x04);
4738c2ecf20Sopenharmony_ci		/* read BER estimation status */
4748c2ecf20Sopenharmony_ci		data = ds3000_readreg(state, 0xf8);
4758c2ecf20Sopenharmony_ci		/* check if BER estimation is ready */
4768c2ecf20Sopenharmony_ci		if ((data & 0x10) == 0) {
4778c2ecf20Sopenharmony_ci			/* this is the number of error bits,
4788c2ecf20Sopenharmony_ci			to calculate the bit error rate
4798c2ecf20Sopenharmony_ci			divide to 8388608 */
4808c2ecf20Sopenharmony_ci			*ber = (ds3000_readreg(state, 0xf7) << 8) |
4818c2ecf20Sopenharmony_ci				ds3000_readreg(state, 0xf6);
4828c2ecf20Sopenharmony_ci			/* start counting error bits */
4838c2ecf20Sopenharmony_ci			/* need to be set twice
4848c2ecf20Sopenharmony_ci			otherwise it fails sometimes */
4858c2ecf20Sopenharmony_ci			data |= 0x10;
4868c2ecf20Sopenharmony_ci			ds3000_writereg(state, 0xf8, data);
4878c2ecf20Sopenharmony_ci			ds3000_writereg(state, 0xf8, data);
4888c2ecf20Sopenharmony_ci		} else
4898c2ecf20Sopenharmony_ci			/* used to indicate that BER estimation
4908c2ecf20Sopenharmony_ci			is not ready, i.e. BER is unknown */
4918c2ecf20Sopenharmony_ci			*ber = 0xffffffff;
4928c2ecf20Sopenharmony_ci		break;
4938c2ecf20Sopenharmony_ci	case SYS_DVBS2:
4948c2ecf20Sopenharmony_ci		/* read the number of LPDC decoded frames */
4958c2ecf20Sopenharmony_ci		lpdc_frames = (ds3000_readreg(state, 0xd7) << 16) |
4968c2ecf20Sopenharmony_ci				(ds3000_readreg(state, 0xd6) << 8) |
4978c2ecf20Sopenharmony_ci				ds3000_readreg(state, 0xd5);
4988c2ecf20Sopenharmony_ci		/* read the number of packets with bad CRC */
4998c2ecf20Sopenharmony_ci		ber_reading = (ds3000_readreg(state, 0xf8) << 8) |
5008c2ecf20Sopenharmony_ci				ds3000_readreg(state, 0xf7);
5018c2ecf20Sopenharmony_ci		if (lpdc_frames > 750) {
5028c2ecf20Sopenharmony_ci			/* clear LPDC frame counters */
5038c2ecf20Sopenharmony_ci			ds3000_writereg(state, 0xd1, 0x01);
5048c2ecf20Sopenharmony_ci			/* clear bad packets counter */
5058c2ecf20Sopenharmony_ci			ds3000_writereg(state, 0xf9, 0x01);
5068c2ecf20Sopenharmony_ci			/* enable bad packets counter */
5078c2ecf20Sopenharmony_ci			ds3000_writereg(state, 0xf9, 0x00);
5088c2ecf20Sopenharmony_ci			/* enable LPDC frame counters */
5098c2ecf20Sopenharmony_ci			ds3000_writereg(state, 0xd1, 0x00);
5108c2ecf20Sopenharmony_ci			*ber = ber_reading;
5118c2ecf20Sopenharmony_ci		} else
5128c2ecf20Sopenharmony_ci			/* used to indicate that BER estimation is not ready,
5138c2ecf20Sopenharmony_ci			i.e. BER is unknown */
5148c2ecf20Sopenharmony_ci			*ber = 0xffffffff;
5158c2ecf20Sopenharmony_ci		break;
5168c2ecf20Sopenharmony_ci	default:
5178c2ecf20Sopenharmony_ci		return -EINVAL;
5188c2ecf20Sopenharmony_ci	}
5198c2ecf20Sopenharmony_ci
5208c2ecf20Sopenharmony_ci	return 0;
5218c2ecf20Sopenharmony_ci}
5228c2ecf20Sopenharmony_ci
5238c2ecf20Sopenharmony_cistatic int ds3000_read_signal_strength(struct dvb_frontend *fe,
5248c2ecf20Sopenharmony_ci						u16 *signal_strength)
5258c2ecf20Sopenharmony_ci{
5268c2ecf20Sopenharmony_ci	if (fe->ops.tuner_ops.get_rf_strength)
5278c2ecf20Sopenharmony_ci		fe->ops.tuner_ops.get_rf_strength(fe, signal_strength);
5288c2ecf20Sopenharmony_ci
5298c2ecf20Sopenharmony_ci	return 0;
5308c2ecf20Sopenharmony_ci}
5318c2ecf20Sopenharmony_ci
5328c2ecf20Sopenharmony_ci/* calculate DS3000 snr value in dB */
5338c2ecf20Sopenharmony_cistatic int ds3000_read_snr(struct dvb_frontend *fe, u16 *snr)
5348c2ecf20Sopenharmony_ci{
5358c2ecf20Sopenharmony_ci	struct ds3000_state *state = fe->demodulator_priv;
5368c2ecf20Sopenharmony_ci	struct dtv_frontend_properties *c = &fe->dtv_property_cache;
5378c2ecf20Sopenharmony_ci	u8 snr_reading, snr_value;
5388c2ecf20Sopenharmony_ci	u32 dvbs2_signal_reading, dvbs2_noise_reading, tmp;
5398c2ecf20Sopenharmony_ci	static const u16 dvbs_snr_tab[] = { /* 20 x Table (rounded up) */
5408c2ecf20Sopenharmony_ci		0x0000, 0x1b13, 0x2aea, 0x3627, 0x3ede, 0x45fe, 0x4c03,
5418c2ecf20Sopenharmony_ci		0x513a, 0x55d4, 0x59f2, 0x5dab, 0x6111, 0x6431, 0x6717,
5428c2ecf20Sopenharmony_ci		0x69c9, 0x6c4e, 0x6eac, 0x70e8, 0x7304, 0x7505
5438c2ecf20Sopenharmony_ci	};
5448c2ecf20Sopenharmony_ci	static const u16 dvbs2_snr_tab[] = { /* 80 x Table (rounded up) */
5458c2ecf20Sopenharmony_ci		0x0000, 0x0bc2, 0x12a3, 0x1785, 0x1b4e, 0x1e65, 0x2103,
5468c2ecf20Sopenharmony_ci		0x2347, 0x2546, 0x2710, 0x28ae, 0x2a28, 0x2b83, 0x2cc5,
5478c2ecf20Sopenharmony_ci		0x2df1, 0x2f09, 0x3010, 0x3109, 0x31f4, 0x32d2, 0x33a6,
5488c2ecf20Sopenharmony_ci		0x3470, 0x3531, 0x35ea, 0x369b, 0x3746, 0x37ea, 0x3888,
5498c2ecf20Sopenharmony_ci		0x3920, 0x39b3, 0x3a42, 0x3acc, 0x3b51, 0x3bd3, 0x3c51,
5508c2ecf20Sopenharmony_ci		0x3ccb, 0x3d42, 0x3db6, 0x3e27, 0x3e95, 0x3f00, 0x3f68,
5518c2ecf20Sopenharmony_ci		0x3fcf, 0x4033, 0x4094, 0x40f4, 0x4151, 0x41ac, 0x4206,
5528c2ecf20Sopenharmony_ci		0x425e, 0x42b4, 0x4308, 0x435b, 0x43ac, 0x43fc, 0x444a,
5538c2ecf20Sopenharmony_ci		0x4497, 0x44e2, 0x452d, 0x4576, 0x45bd, 0x4604, 0x4649,
5548c2ecf20Sopenharmony_ci		0x468e, 0x46d1, 0x4713, 0x4755, 0x4795, 0x47d4, 0x4813,
5558c2ecf20Sopenharmony_ci		0x4851, 0x488d, 0x48c9, 0x4904, 0x493f, 0x4978, 0x49b1,
5568c2ecf20Sopenharmony_ci		0x49e9, 0x4a20, 0x4a57
5578c2ecf20Sopenharmony_ci	};
5588c2ecf20Sopenharmony_ci
5598c2ecf20Sopenharmony_ci	dprintk("%s()\n", __func__);
5608c2ecf20Sopenharmony_ci
5618c2ecf20Sopenharmony_ci	switch (c->delivery_system) {
5628c2ecf20Sopenharmony_ci	case SYS_DVBS:
5638c2ecf20Sopenharmony_ci		snr_reading = ds3000_readreg(state, 0xff);
5648c2ecf20Sopenharmony_ci		snr_reading /= 8;
5658c2ecf20Sopenharmony_ci		if (snr_reading == 0)
5668c2ecf20Sopenharmony_ci			*snr = 0x0000;
5678c2ecf20Sopenharmony_ci		else {
5688c2ecf20Sopenharmony_ci			if (snr_reading > 20)
5698c2ecf20Sopenharmony_ci				snr_reading = 20;
5708c2ecf20Sopenharmony_ci			snr_value = dvbs_snr_tab[snr_reading - 1] * 10 / 23026;
5718c2ecf20Sopenharmony_ci			/* cook the value to be suitable for szap-s2
5728c2ecf20Sopenharmony_ci			human readable output */
5738c2ecf20Sopenharmony_ci			*snr = snr_value * 8 * 655;
5748c2ecf20Sopenharmony_ci		}
5758c2ecf20Sopenharmony_ci		dprintk("%s: raw / cooked = 0x%02x / 0x%04x\n", __func__,
5768c2ecf20Sopenharmony_ci				snr_reading, *snr);
5778c2ecf20Sopenharmony_ci		break;
5788c2ecf20Sopenharmony_ci	case SYS_DVBS2:
5798c2ecf20Sopenharmony_ci		dvbs2_noise_reading = (ds3000_readreg(state, 0x8c) & 0x3f) +
5808c2ecf20Sopenharmony_ci				(ds3000_readreg(state, 0x8d) << 4);
5818c2ecf20Sopenharmony_ci		dvbs2_signal_reading = ds3000_readreg(state, 0x8e);
5828c2ecf20Sopenharmony_ci		tmp = dvbs2_signal_reading * dvbs2_signal_reading >> 1;
5838c2ecf20Sopenharmony_ci		if (tmp == 0) {
5848c2ecf20Sopenharmony_ci			*snr = 0x0000;
5858c2ecf20Sopenharmony_ci			return 0;
5868c2ecf20Sopenharmony_ci		}
5878c2ecf20Sopenharmony_ci		if (dvbs2_noise_reading == 0) {
5888c2ecf20Sopenharmony_ci			snr_value = 0x0013;
5898c2ecf20Sopenharmony_ci			/* cook the value to be suitable for szap-s2
5908c2ecf20Sopenharmony_ci			human readable output */
5918c2ecf20Sopenharmony_ci			*snr = 0xffff;
5928c2ecf20Sopenharmony_ci			return 0;
5938c2ecf20Sopenharmony_ci		}
5948c2ecf20Sopenharmony_ci		if (tmp > dvbs2_noise_reading) {
5958c2ecf20Sopenharmony_ci			snr_reading = tmp / dvbs2_noise_reading;
5968c2ecf20Sopenharmony_ci			if (snr_reading > 80)
5978c2ecf20Sopenharmony_ci				snr_reading = 80;
5988c2ecf20Sopenharmony_ci			snr_value = dvbs2_snr_tab[snr_reading - 1] / 1000;
5998c2ecf20Sopenharmony_ci			/* cook the value to be suitable for szap-s2
6008c2ecf20Sopenharmony_ci			human readable output */
6018c2ecf20Sopenharmony_ci			*snr = snr_value * 5 * 655;
6028c2ecf20Sopenharmony_ci		} else {
6038c2ecf20Sopenharmony_ci			snr_reading = dvbs2_noise_reading / tmp;
6048c2ecf20Sopenharmony_ci			if (snr_reading > 80)
6058c2ecf20Sopenharmony_ci				snr_reading = 80;
6068c2ecf20Sopenharmony_ci			*snr = -(dvbs2_snr_tab[snr_reading - 1] / 1000);
6078c2ecf20Sopenharmony_ci		}
6088c2ecf20Sopenharmony_ci		dprintk("%s: raw / cooked = 0x%02x / 0x%04x\n", __func__,
6098c2ecf20Sopenharmony_ci				snr_reading, *snr);
6108c2ecf20Sopenharmony_ci		break;
6118c2ecf20Sopenharmony_ci	default:
6128c2ecf20Sopenharmony_ci		return -EINVAL;
6138c2ecf20Sopenharmony_ci	}
6148c2ecf20Sopenharmony_ci
6158c2ecf20Sopenharmony_ci	return 0;
6168c2ecf20Sopenharmony_ci}
6178c2ecf20Sopenharmony_ci
6188c2ecf20Sopenharmony_ci/* read DS3000 uncorrected blocks */
6198c2ecf20Sopenharmony_cistatic int ds3000_read_ucblocks(struct dvb_frontend *fe, u32 *ucblocks)
6208c2ecf20Sopenharmony_ci{
6218c2ecf20Sopenharmony_ci	struct ds3000_state *state = fe->demodulator_priv;
6228c2ecf20Sopenharmony_ci	struct dtv_frontend_properties *c = &fe->dtv_property_cache;
6238c2ecf20Sopenharmony_ci	u8 data;
6248c2ecf20Sopenharmony_ci	u16 _ucblocks;
6258c2ecf20Sopenharmony_ci
6268c2ecf20Sopenharmony_ci	dprintk("%s()\n", __func__);
6278c2ecf20Sopenharmony_ci
6288c2ecf20Sopenharmony_ci	switch (c->delivery_system) {
6298c2ecf20Sopenharmony_ci	case SYS_DVBS:
6308c2ecf20Sopenharmony_ci		*ucblocks = (ds3000_readreg(state, 0xf5) << 8) |
6318c2ecf20Sopenharmony_ci				ds3000_readreg(state, 0xf4);
6328c2ecf20Sopenharmony_ci		data = ds3000_readreg(state, 0xf8);
6338c2ecf20Sopenharmony_ci		/* clear packet counters */
6348c2ecf20Sopenharmony_ci		data &= ~0x20;
6358c2ecf20Sopenharmony_ci		ds3000_writereg(state, 0xf8, data);
6368c2ecf20Sopenharmony_ci		/* enable packet counters */
6378c2ecf20Sopenharmony_ci		data |= 0x20;
6388c2ecf20Sopenharmony_ci		ds3000_writereg(state, 0xf8, data);
6398c2ecf20Sopenharmony_ci		break;
6408c2ecf20Sopenharmony_ci	case SYS_DVBS2:
6418c2ecf20Sopenharmony_ci		_ucblocks = (ds3000_readreg(state, 0xe2) << 8) |
6428c2ecf20Sopenharmony_ci				ds3000_readreg(state, 0xe1);
6438c2ecf20Sopenharmony_ci		if (_ucblocks > state->prevUCBS2)
6448c2ecf20Sopenharmony_ci			*ucblocks = _ucblocks - state->prevUCBS2;
6458c2ecf20Sopenharmony_ci		else
6468c2ecf20Sopenharmony_ci			*ucblocks = state->prevUCBS2 - _ucblocks;
6478c2ecf20Sopenharmony_ci		state->prevUCBS2 = _ucblocks;
6488c2ecf20Sopenharmony_ci		break;
6498c2ecf20Sopenharmony_ci	default:
6508c2ecf20Sopenharmony_ci		return -EINVAL;
6518c2ecf20Sopenharmony_ci	}
6528c2ecf20Sopenharmony_ci
6538c2ecf20Sopenharmony_ci	return 0;
6548c2ecf20Sopenharmony_ci}
6558c2ecf20Sopenharmony_ci
6568c2ecf20Sopenharmony_cistatic int ds3000_set_tone(struct dvb_frontend *fe, enum fe_sec_tone_mode tone)
6578c2ecf20Sopenharmony_ci{
6588c2ecf20Sopenharmony_ci	struct ds3000_state *state = fe->demodulator_priv;
6598c2ecf20Sopenharmony_ci	u8 data;
6608c2ecf20Sopenharmony_ci
6618c2ecf20Sopenharmony_ci	dprintk("%s(%d)\n", __func__, tone);
6628c2ecf20Sopenharmony_ci	if ((tone != SEC_TONE_ON) && (tone != SEC_TONE_OFF)) {
6638c2ecf20Sopenharmony_ci		printk(KERN_ERR "%s: Invalid, tone=%d\n", __func__, tone);
6648c2ecf20Sopenharmony_ci		return -EINVAL;
6658c2ecf20Sopenharmony_ci	}
6668c2ecf20Sopenharmony_ci
6678c2ecf20Sopenharmony_ci	data = ds3000_readreg(state, 0xa2);
6688c2ecf20Sopenharmony_ci	data &= ~0xc0;
6698c2ecf20Sopenharmony_ci	ds3000_writereg(state, 0xa2, data);
6708c2ecf20Sopenharmony_ci
6718c2ecf20Sopenharmony_ci	switch (tone) {
6728c2ecf20Sopenharmony_ci	case SEC_TONE_ON:
6738c2ecf20Sopenharmony_ci		dprintk("%s: setting tone on\n", __func__);
6748c2ecf20Sopenharmony_ci		data = ds3000_readreg(state, 0xa1);
6758c2ecf20Sopenharmony_ci		data &= ~0x43;
6768c2ecf20Sopenharmony_ci		data |= 0x04;
6778c2ecf20Sopenharmony_ci		ds3000_writereg(state, 0xa1, data);
6788c2ecf20Sopenharmony_ci		break;
6798c2ecf20Sopenharmony_ci	case SEC_TONE_OFF:
6808c2ecf20Sopenharmony_ci		dprintk("%s: setting tone off\n", __func__);
6818c2ecf20Sopenharmony_ci		data = ds3000_readreg(state, 0xa2);
6828c2ecf20Sopenharmony_ci		data |= 0x80;
6838c2ecf20Sopenharmony_ci		ds3000_writereg(state, 0xa2, data);
6848c2ecf20Sopenharmony_ci		break;
6858c2ecf20Sopenharmony_ci	}
6868c2ecf20Sopenharmony_ci
6878c2ecf20Sopenharmony_ci	return 0;
6888c2ecf20Sopenharmony_ci}
6898c2ecf20Sopenharmony_ci
6908c2ecf20Sopenharmony_cistatic int ds3000_send_diseqc_msg(struct dvb_frontend *fe,
6918c2ecf20Sopenharmony_ci				struct dvb_diseqc_master_cmd *d)
6928c2ecf20Sopenharmony_ci{
6938c2ecf20Sopenharmony_ci	struct ds3000_state *state = fe->demodulator_priv;
6948c2ecf20Sopenharmony_ci	int i;
6958c2ecf20Sopenharmony_ci	u8 data;
6968c2ecf20Sopenharmony_ci
6978c2ecf20Sopenharmony_ci	/* Dump DiSEqC message */
6988c2ecf20Sopenharmony_ci	dprintk("%s(", __func__);
6998c2ecf20Sopenharmony_ci	for (i = 0 ; i < d->msg_len;) {
7008c2ecf20Sopenharmony_ci		dprintk("0x%02x", d->msg[i]);
7018c2ecf20Sopenharmony_ci		if (++i < d->msg_len)
7028c2ecf20Sopenharmony_ci			dprintk(", ");
7038c2ecf20Sopenharmony_ci	}
7048c2ecf20Sopenharmony_ci
7058c2ecf20Sopenharmony_ci	/* enable DiSEqC message send pin */
7068c2ecf20Sopenharmony_ci	data = ds3000_readreg(state, 0xa2);
7078c2ecf20Sopenharmony_ci	data &= ~0xc0;
7088c2ecf20Sopenharmony_ci	ds3000_writereg(state, 0xa2, data);
7098c2ecf20Sopenharmony_ci
7108c2ecf20Sopenharmony_ci	/* DiSEqC message */
7118c2ecf20Sopenharmony_ci	for (i = 0; i < d->msg_len; i++)
7128c2ecf20Sopenharmony_ci		ds3000_writereg(state, 0xa3 + i, d->msg[i]);
7138c2ecf20Sopenharmony_ci
7148c2ecf20Sopenharmony_ci	data = ds3000_readreg(state, 0xa1);
7158c2ecf20Sopenharmony_ci	/* clear DiSEqC message length and status,
7168c2ecf20Sopenharmony_ci	enable DiSEqC message send */
7178c2ecf20Sopenharmony_ci	data &= ~0xf8;
7188c2ecf20Sopenharmony_ci	/* set DiSEqC mode, modulation active during 33 pulses,
7198c2ecf20Sopenharmony_ci	set DiSEqC message length */
7208c2ecf20Sopenharmony_ci	data |= ((d->msg_len - 1) << 3) | 0x07;
7218c2ecf20Sopenharmony_ci	ds3000_writereg(state, 0xa1, data);
7228c2ecf20Sopenharmony_ci
7238c2ecf20Sopenharmony_ci	/* wait up to 150ms for DiSEqC transmission to complete */
7248c2ecf20Sopenharmony_ci	for (i = 0; i < 15; i++) {
7258c2ecf20Sopenharmony_ci		data = ds3000_readreg(state, 0xa1);
7268c2ecf20Sopenharmony_ci		if ((data & 0x40) == 0)
7278c2ecf20Sopenharmony_ci			break;
7288c2ecf20Sopenharmony_ci		msleep(10);
7298c2ecf20Sopenharmony_ci	}
7308c2ecf20Sopenharmony_ci
7318c2ecf20Sopenharmony_ci	/* DiSEqC timeout after 150ms */
7328c2ecf20Sopenharmony_ci	if (i == 15) {
7338c2ecf20Sopenharmony_ci		data = ds3000_readreg(state, 0xa1);
7348c2ecf20Sopenharmony_ci		data &= ~0x80;
7358c2ecf20Sopenharmony_ci		data |= 0x40;
7368c2ecf20Sopenharmony_ci		ds3000_writereg(state, 0xa1, data);
7378c2ecf20Sopenharmony_ci
7388c2ecf20Sopenharmony_ci		data = ds3000_readreg(state, 0xa2);
7398c2ecf20Sopenharmony_ci		data &= ~0xc0;
7408c2ecf20Sopenharmony_ci		data |= 0x80;
7418c2ecf20Sopenharmony_ci		ds3000_writereg(state, 0xa2, data);
7428c2ecf20Sopenharmony_ci
7438c2ecf20Sopenharmony_ci		return -ETIMEDOUT;
7448c2ecf20Sopenharmony_ci	}
7458c2ecf20Sopenharmony_ci
7468c2ecf20Sopenharmony_ci	data = ds3000_readreg(state, 0xa2);
7478c2ecf20Sopenharmony_ci	data &= ~0xc0;
7488c2ecf20Sopenharmony_ci	data |= 0x80;
7498c2ecf20Sopenharmony_ci	ds3000_writereg(state, 0xa2, data);
7508c2ecf20Sopenharmony_ci
7518c2ecf20Sopenharmony_ci	return 0;
7528c2ecf20Sopenharmony_ci}
7538c2ecf20Sopenharmony_ci
7548c2ecf20Sopenharmony_ci/* Send DiSEqC burst */
7558c2ecf20Sopenharmony_cistatic int ds3000_diseqc_send_burst(struct dvb_frontend *fe,
7568c2ecf20Sopenharmony_ci				    enum fe_sec_mini_cmd burst)
7578c2ecf20Sopenharmony_ci{
7588c2ecf20Sopenharmony_ci	struct ds3000_state *state = fe->demodulator_priv;
7598c2ecf20Sopenharmony_ci	int i;
7608c2ecf20Sopenharmony_ci	u8 data;
7618c2ecf20Sopenharmony_ci
7628c2ecf20Sopenharmony_ci	dprintk("%s()\n", __func__);
7638c2ecf20Sopenharmony_ci
7648c2ecf20Sopenharmony_ci	data = ds3000_readreg(state, 0xa2);
7658c2ecf20Sopenharmony_ci	data &= ~0xc0;
7668c2ecf20Sopenharmony_ci	ds3000_writereg(state, 0xa2, data);
7678c2ecf20Sopenharmony_ci
7688c2ecf20Sopenharmony_ci	/* DiSEqC burst */
7698c2ecf20Sopenharmony_ci	if (burst == SEC_MINI_A)
7708c2ecf20Sopenharmony_ci		/* Unmodulated tone burst */
7718c2ecf20Sopenharmony_ci		ds3000_writereg(state, 0xa1, 0x02);
7728c2ecf20Sopenharmony_ci	else if (burst == SEC_MINI_B)
7738c2ecf20Sopenharmony_ci		/* Modulated tone burst */
7748c2ecf20Sopenharmony_ci		ds3000_writereg(state, 0xa1, 0x01);
7758c2ecf20Sopenharmony_ci	else
7768c2ecf20Sopenharmony_ci		return -EINVAL;
7778c2ecf20Sopenharmony_ci
7788c2ecf20Sopenharmony_ci	msleep(13);
7798c2ecf20Sopenharmony_ci	for (i = 0; i < 5; i++) {
7808c2ecf20Sopenharmony_ci		data = ds3000_readreg(state, 0xa1);
7818c2ecf20Sopenharmony_ci		if ((data & 0x40) == 0)
7828c2ecf20Sopenharmony_ci			break;
7838c2ecf20Sopenharmony_ci		msleep(1);
7848c2ecf20Sopenharmony_ci	}
7858c2ecf20Sopenharmony_ci
7868c2ecf20Sopenharmony_ci	if (i == 5) {
7878c2ecf20Sopenharmony_ci		data = ds3000_readreg(state, 0xa1);
7888c2ecf20Sopenharmony_ci		data &= ~0x80;
7898c2ecf20Sopenharmony_ci		data |= 0x40;
7908c2ecf20Sopenharmony_ci		ds3000_writereg(state, 0xa1, data);
7918c2ecf20Sopenharmony_ci
7928c2ecf20Sopenharmony_ci		data = ds3000_readreg(state, 0xa2);
7938c2ecf20Sopenharmony_ci		data &= ~0xc0;
7948c2ecf20Sopenharmony_ci		data |= 0x80;
7958c2ecf20Sopenharmony_ci		ds3000_writereg(state, 0xa2, data);
7968c2ecf20Sopenharmony_ci
7978c2ecf20Sopenharmony_ci		return -ETIMEDOUT;
7988c2ecf20Sopenharmony_ci	}
7998c2ecf20Sopenharmony_ci
8008c2ecf20Sopenharmony_ci	data = ds3000_readreg(state, 0xa2);
8018c2ecf20Sopenharmony_ci	data &= ~0xc0;
8028c2ecf20Sopenharmony_ci	data |= 0x80;
8038c2ecf20Sopenharmony_ci	ds3000_writereg(state, 0xa2, data);
8048c2ecf20Sopenharmony_ci
8058c2ecf20Sopenharmony_ci	return 0;
8068c2ecf20Sopenharmony_ci}
8078c2ecf20Sopenharmony_ci
8088c2ecf20Sopenharmony_cistatic void ds3000_release(struct dvb_frontend *fe)
8098c2ecf20Sopenharmony_ci{
8108c2ecf20Sopenharmony_ci	struct ds3000_state *state = fe->demodulator_priv;
8118c2ecf20Sopenharmony_ci
8128c2ecf20Sopenharmony_ci	if (state->config->set_lock_led)
8138c2ecf20Sopenharmony_ci		state->config->set_lock_led(fe, 0);
8148c2ecf20Sopenharmony_ci
8158c2ecf20Sopenharmony_ci	dprintk("%s\n", __func__);
8168c2ecf20Sopenharmony_ci	kfree(state);
8178c2ecf20Sopenharmony_ci}
8188c2ecf20Sopenharmony_ci
8198c2ecf20Sopenharmony_cistatic const struct dvb_frontend_ops ds3000_ops;
8208c2ecf20Sopenharmony_ci
8218c2ecf20Sopenharmony_cistruct dvb_frontend *ds3000_attach(const struct ds3000_config *config,
8228c2ecf20Sopenharmony_ci				    struct i2c_adapter *i2c)
8238c2ecf20Sopenharmony_ci{
8248c2ecf20Sopenharmony_ci	struct ds3000_state *state;
8258c2ecf20Sopenharmony_ci	int ret;
8268c2ecf20Sopenharmony_ci
8278c2ecf20Sopenharmony_ci	dprintk("%s\n", __func__);
8288c2ecf20Sopenharmony_ci
8298c2ecf20Sopenharmony_ci	/* allocate memory for the internal state */
8308c2ecf20Sopenharmony_ci	state = kzalloc(sizeof(*state), GFP_KERNEL);
8318c2ecf20Sopenharmony_ci	if (!state)
8328c2ecf20Sopenharmony_ci		return NULL;
8338c2ecf20Sopenharmony_ci
8348c2ecf20Sopenharmony_ci	state->config = config;
8358c2ecf20Sopenharmony_ci	state->i2c = i2c;
8368c2ecf20Sopenharmony_ci	state->prevUCBS2 = 0;
8378c2ecf20Sopenharmony_ci
8388c2ecf20Sopenharmony_ci	/* check if the demod is present */
8398c2ecf20Sopenharmony_ci	ret = ds3000_readreg(state, 0x00) & 0xfe;
8408c2ecf20Sopenharmony_ci	if (ret != 0xe0) {
8418c2ecf20Sopenharmony_ci		kfree(state);
8428c2ecf20Sopenharmony_ci		printk(KERN_ERR "Invalid probe, probably not a DS3000\n");
8438c2ecf20Sopenharmony_ci		return NULL;
8448c2ecf20Sopenharmony_ci	}
8458c2ecf20Sopenharmony_ci
8468c2ecf20Sopenharmony_ci	printk(KERN_INFO "DS3000 chip version: %d.%d attached.\n",
8478c2ecf20Sopenharmony_ci			ds3000_readreg(state, 0x02),
8488c2ecf20Sopenharmony_ci			ds3000_readreg(state, 0x01));
8498c2ecf20Sopenharmony_ci
8508c2ecf20Sopenharmony_ci	memcpy(&state->frontend.ops, &ds3000_ops,
8518c2ecf20Sopenharmony_ci			sizeof(struct dvb_frontend_ops));
8528c2ecf20Sopenharmony_ci	state->frontend.demodulator_priv = state;
8538c2ecf20Sopenharmony_ci
8548c2ecf20Sopenharmony_ci	/*
8558c2ecf20Sopenharmony_ci	 * Some devices like T480 starts with voltage on. Be sure
8568c2ecf20Sopenharmony_ci	 * to turn voltage off during init, as this can otherwise
8578c2ecf20Sopenharmony_ci	 * interfere with Unicable SCR systems.
8588c2ecf20Sopenharmony_ci	 */
8598c2ecf20Sopenharmony_ci	ds3000_set_voltage(&state->frontend, SEC_VOLTAGE_OFF);
8608c2ecf20Sopenharmony_ci	return &state->frontend;
8618c2ecf20Sopenharmony_ci}
8628c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(ds3000_attach);
8638c2ecf20Sopenharmony_ci
8648c2ecf20Sopenharmony_cistatic int ds3000_set_carrier_offset(struct dvb_frontend *fe,
8658c2ecf20Sopenharmony_ci					s32 carrier_offset_khz)
8668c2ecf20Sopenharmony_ci{
8678c2ecf20Sopenharmony_ci	struct ds3000_state *state = fe->demodulator_priv;
8688c2ecf20Sopenharmony_ci	s32 tmp;
8698c2ecf20Sopenharmony_ci
8708c2ecf20Sopenharmony_ci	tmp = carrier_offset_khz;
8718c2ecf20Sopenharmony_ci	tmp *= 65536;
8728c2ecf20Sopenharmony_ci	tmp = (2 * tmp + DS3000_SAMPLE_RATE) / (2 * DS3000_SAMPLE_RATE);
8738c2ecf20Sopenharmony_ci
8748c2ecf20Sopenharmony_ci	if (tmp < 0)
8758c2ecf20Sopenharmony_ci		tmp += 65536;
8768c2ecf20Sopenharmony_ci
8778c2ecf20Sopenharmony_ci	ds3000_writereg(state, 0x5f, tmp >> 8);
8788c2ecf20Sopenharmony_ci	ds3000_writereg(state, 0x5e, tmp & 0xff);
8798c2ecf20Sopenharmony_ci
8808c2ecf20Sopenharmony_ci	return 0;
8818c2ecf20Sopenharmony_ci}
8828c2ecf20Sopenharmony_ci
8838c2ecf20Sopenharmony_cistatic int ds3000_set_frontend(struct dvb_frontend *fe)
8848c2ecf20Sopenharmony_ci{
8858c2ecf20Sopenharmony_ci	struct ds3000_state *state = fe->demodulator_priv;
8868c2ecf20Sopenharmony_ci	struct dtv_frontend_properties *c = &fe->dtv_property_cache;
8878c2ecf20Sopenharmony_ci
8888c2ecf20Sopenharmony_ci	int i;
8898c2ecf20Sopenharmony_ci	enum fe_status status;
8908c2ecf20Sopenharmony_ci	s32 offset_khz;
8918c2ecf20Sopenharmony_ci	u32 frequency;
8928c2ecf20Sopenharmony_ci	u16 value;
8938c2ecf20Sopenharmony_ci
8948c2ecf20Sopenharmony_ci	dprintk("%s() ", __func__);
8958c2ecf20Sopenharmony_ci
8968c2ecf20Sopenharmony_ci	if (state->config->set_ts_params)
8978c2ecf20Sopenharmony_ci		state->config->set_ts_params(fe, 0);
8988c2ecf20Sopenharmony_ci	/* Tune */
8998c2ecf20Sopenharmony_ci	if (fe->ops.tuner_ops.set_params)
9008c2ecf20Sopenharmony_ci		fe->ops.tuner_ops.set_params(fe);
9018c2ecf20Sopenharmony_ci
9028c2ecf20Sopenharmony_ci	/* ds3000 global reset */
9038c2ecf20Sopenharmony_ci	ds3000_writereg(state, 0x07, 0x80);
9048c2ecf20Sopenharmony_ci	ds3000_writereg(state, 0x07, 0x00);
9058c2ecf20Sopenharmony_ci	/* ds3000 built-in uC reset */
9068c2ecf20Sopenharmony_ci	ds3000_writereg(state, 0xb2, 0x01);
9078c2ecf20Sopenharmony_ci	/* ds3000 software reset */
9088c2ecf20Sopenharmony_ci	ds3000_writereg(state, 0x00, 0x01);
9098c2ecf20Sopenharmony_ci
9108c2ecf20Sopenharmony_ci	switch (c->delivery_system) {
9118c2ecf20Sopenharmony_ci	case SYS_DVBS:
9128c2ecf20Sopenharmony_ci		/* initialise the demod in DVB-S mode */
9138c2ecf20Sopenharmony_ci		for (i = 0; i < sizeof(ds3000_dvbs_init_tab); i += 2)
9148c2ecf20Sopenharmony_ci			ds3000_writereg(state,
9158c2ecf20Sopenharmony_ci				ds3000_dvbs_init_tab[i],
9168c2ecf20Sopenharmony_ci				ds3000_dvbs_init_tab[i + 1]);
9178c2ecf20Sopenharmony_ci		value = ds3000_readreg(state, 0xfe);
9188c2ecf20Sopenharmony_ci		value &= 0xc0;
9198c2ecf20Sopenharmony_ci		value |= 0x1b;
9208c2ecf20Sopenharmony_ci		ds3000_writereg(state, 0xfe, value);
9218c2ecf20Sopenharmony_ci		break;
9228c2ecf20Sopenharmony_ci	case SYS_DVBS2:
9238c2ecf20Sopenharmony_ci		/* initialise the demod in DVB-S2 mode */
9248c2ecf20Sopenharmony_ci		for (i = 0; i < sizeof(ds3000_dvbs2_init_tab); i += 2)
9258c2ecf20Sopenharmony_ci			ds3000_writereg(state,
9268c2ecf20Sopenharmony_ci				ds3000_dvbs2_init_tab[i],
9278c2ecf20Sopenharmony_ci				ds3000_dvbs2_init_tab[i + 1]);
9288c2ecf20Sopenharmony_ci		if (c->symbol_rate >= 30000000)
9298c2ecf20Sopenharmony_ci			ds3000_writereg(state, 0xfe, 0x54);
9308c2ecf20Sopenharmony_ci		else
9318c2ecf20Sopenharmony_ci			ds3000_writereg(state, 0xfe, 0x98);
9328c2ecf20Sopenharmony_ci		break;
9338c2ecf20Sopenharmony_ci	default:
9348c2ecf20Sopenharmony_ci		return -EINVAL;
9358c2ecf20Sopenharmony_ci	}
9368c2ecf20Sopenharmony_ci
9378c2ecf20Sopenharmony_ci	/* enable 27MHz clock output */
9388c2ecf20Sopenharmony_ci	ds3000_writereg(state, 0x29, 0x80);
9398c2ecf20Sopenharmony_ci	/* enable ac coupling */
9408c2ecf20Sopenharmony_ci	ds3000_writereg(state, 0x25, 0x8a);
9418c2ecf20Sopenharmony_ci
9428c2ecf20Sopenharmony_ci	if ((c->symbol_rate < ds3000_ops.info.symbol_rate_min) ||
9438c2ecf20Sopenharmony_ci			(c->symbol_rate > ds3000_ops.info.symbol_rate_max)) {
9448c2ecf20Sopenharmony_ci		dprintk("%s() symbol_rate %u out of range (%u ... %u)\n",
9458c2ecf20Sopenharmony_ci				__func__, c->symbol_rate,
9468c2ecf20Sopenharmony_ci				ds3000_ops.info.symbol_rate_min,
9478c2ecf20Sopenharmony_ci				ds3000_ops.info.symbol_rate_max);
9488c2ecf20Sopenharmony_ci		return -EINVAL;
9498c2ecf20Sopenharmony_ci	}
9508c2ecf20Sopenharmony_ci
9518c2ecf20Sopenharmony_ci	/* enhance symbol rate performance */
9528c2ecf20Sopenharmony_ci	if ((c->symbol_rate / 1000) <= 5000) {
9538c2ecf20Sopenharmony_ci		value = 29777 / (c->symbol_rate / 1000) + 1;
9548c2ecf20Sopenharmony_ci		if (value % 2 != 0)
9558c2ecf20Sopenharmony_ci			value++;
9568c2ecf20Sopenharmony_ci		ds3000_writereg(state, 0xc3, 0x0d);
9578c2ecf20Sopenharmony_ci		ds3000_writereg(state, 0xc8, value);
9588c2ecf20Sopenharmony_ci		ds3000_writereg(state, 0xc4, 0x10);
9598c2ecf20Sopenharmony_ci		ds3000_writereg(state, 0xc7, 0x0e);
9608c2ecf20Sopenharmony_ci	} else if ((c->symbol_rate / 1000) <= 10000) {
9618c2ecf20Sopenharmony_ci		value = 92166 / (c->symbol_rate / 1000) + 1;
9628c2ecf20Sopenharmony_ci		if (value % 2 != 0)
9638c2ecf20Sopenharmony_ci			value++;
9648c2ecf20Sopenharmony_ci		ds3000_writereg(state, 0xc3, 0x07);
9658c2ecf20Sopenharmony_ci		ds3000_writereg(state, 0xc8, value);
9668c2ecf20Sopenharmony_ci		ds3000_writereg(state, 0xc4, 0x09);
9678c2ecf20Sopenharmony_ci		ds3000_writereg(state, 0xc7, 0x12);
9688c2ecf20Sopenharmony_ci	} else if ((c->symbol_rate / 1000) <= 20000) {
9698c2ecf20Sopenharmony_ci		value = 64516 / (c->symbol_rate / 1000) + 1;
9708c2ecf20Sopenharmony_ci		ds3000_writereg(state, 0xc3, value);
9718c2ecf20Sopenharmony_ci		ds3000_writereg(state, 0xc8, 0x0e);
9728c2ecf20Sopenharmony_ci		ds3000_writereg(state, 0xc4, 0x07);
9738c2ecf20Sopenharmony_ci		ds3000_writereg(state, 0xc7, 0x18);
9748c2ecf20Sopenharmony_ci	} else {
9758c2ecf20Sopenharmony_ci		value = 129032 / (c->symbol_rate / 1000) + 1;
9768c2ecf20Sopenharmony_ci		ds3000_writereg(state, 0xc3, value);
9778c2ecf20Sopenharmony_ci		ds3000_writereg(state, 0xc8, 0x0a);
9788c2ecf20Sopenharmony_ci		ds3000_writereg(state, 0xc4, 0x05);
9798c2ecf20Sopenharmony_ci		ds3000_writereg(state, 0xc7, 0x24);
9808c2ecf20Sopenharmony_ci	}
9818c2ecf20Sopenharmony_ci
9828c2ecf20Sopenharmony_ci	/* normalized symbol rate rounded to the closest integer */
9838c2ecf20Sopenharmony_ci	value = (((c->symbol_rate / 1000) << 16) +
9848c2ecf20Sopenharmony_ci			(DS3000_SAMPLE_RATE / 2)) / DS3000_SAMPLE_RATE;
9858c2ecf20Sopenharmony_ci	ds3000_writereg(state, 0x61, value & 0x00ff);
9868c2ecf20Sopenharmony_ci	ds3000_writereg(state, 0x62, (value & 0xff00) >> 8);
9878c2ecf20Sopenharmony_ci
9888c2ecf20Sopenharmony_ci	/* co-channel interference cancellation disabled */
9898c2ecf20Sopenharmony_ci	ds3000_writereg(state, 0x56, 0x00);
9908c2ecf20Sopenharmony_ci
9918c2ecf20Sopenharmony_ci	/* equalizer disabled */
9928c2ecf20Sopenharmony_ci	ds3000_writereg(state, 0x76, 0x00);
9938c2ecf20Sopenharmony_ci
9948c2ecf20Sopenharmony_ci	/*ds3000_writereg(state, 0x08, 0x03);
9958c2ecf20Sopenharmony_ci	ds3000_writereg(state, 0xfd, 0x22);
9968c2ecf20Sopenharmony_ci	ds3000_writereg(state, 0x08, 0x07);
9978c2ecf20Sopenharmony_ci	ds3000_writereg(state, 0xfd, 0x42);
9988c2ecf20Sopenharmony_ci	ds3000_writereg(state, 0x08, 0x07);*/
9998c2ecf20Sopenharmony_ci
10008c2ecf20Sopenharmony_ci	if (state->config->ci_mode) {
10018c2ecf20Sopenharmony_ci		switch (c->delivery_system) {
10028c2ecf20Sopenharmony_ci		case SYS_DVBS:
10038c2ecf20Sopenharmony_ci		default:
10048c2ecf20Sopenharmony_ci			ds3000_writereg(state, 0xfd, 0x80);
10058c2ecf20Sopenharmony_ci		break;
10068c2ecf20Sopenharmony_ci		case SYS_DVBS2:
10078c2ecf20Sopenharmony_ci			ds3000_writereg(state, 0xfd, 0x01);
10088c2ecf20Sopenharmony_ci			break;
10098c2ecf20Sopenharmony_ci		}
10108c2ecf20Sopenharmony_ci	}
10118c2ecf20Sopenharmony_ci
10128c2ecf20Sopenharmony_ci	/* ds3000 out of software reset */
10138c2ecf20Sopenharmony_ci	ds3000_writereg(state, 0x00, 0x00);
10148c2ecf20Sopenharmony_ci	/* start ds3000 built-in uC */
10158c2ecf20Sopenharmony_ci	ds3000_writereg(state, 0xb2, 0x00);
10168c2ecf20Sopenharmony_ci
10178c2ecf20Sopenharmony_ci	if (fe->ops.tuner_ops.get_frequency) {
10188c2ecf20Sopenharmony_ci		fe->ops.tuner_ops.get_frequency(fe, &frequency);
10198c2ecf20Sopenharmony_ci		offset_khz = frequency - c->frequency;
10208c2ecf20Sopenharmony_ci		ds3000_set_carrier_offset(fe, offset_khz);
10218c2ecf20Sopenharmony_ci	}
10228c2ecf20Sopenharmony_ci
10238c2ecf20Sopenharmony_ci	for (i = 0; i < 30 ; i++) {
10248c2ecf20Sopenharmony_ci		ds3000_read_status(fe, &status);
10258c2ecf20Sopenharmony_ci		if (status & FE_HAS_LOCK)
10268c2ecf20Sopenharmony_ci			break;
10278c2ecf20Sopenharmony_ci
10288c2ecf20Sopenharmony_ci		msleep(10);
10298c2ecf20Sopenharmony_ci	}
10308c2ecf20Sopenharmony_ci
10318c2ecf20Sopenharmony_ci	return 0;
10328c2ecf20Sopenharmony_ci}
10338c2ecf20Sopenharmony_ci
10348c2ecf20Sopenharmony_cistatic int ds3000_tune(struct dvb_frontend *fe,
10358c2ecf20Sopenharmony_ci			bool re_tune,
10368c2ecf20Sopenharmony_ci			unsigned int mode_flags,
10378c2ecf20Sopenharmony_ci			unsigned int *delay,
10388c2ecf20Sopenharmony_ci			enum fe_status *status)
10398c2ecf20Sopenharmony_ci{
10408c2ecf20Sopenharmony_ci	if (re_tune) {
10418c2ecf20Sopenharmony_ci		int ret = ds3000_set_frontend(fe);
10428c2ecf20Sopenharmony_ci		if (ret)
10438c2ecf20Sopenharmony_ci			return ret;
10448c2ecf20Sopenharmony_ci	}
10458c2ecf20Sopenharmony_ci
10468c2ecf20Sopenharmony_ci	*delay = HZ / 5;
10478c2ecf20Sopenharmony_ci
10488c2ecf20Sopenharmony_ci	return ds3000_read_status(fe, status);
10498c2ecf20Sopenharmony_ci}
10508c2ecf20Sopenharmony_ci
10518c2ecf20Sopenharmony_cistatic enum dvbfe_algo ds3000_get_algo(struct dvb_frontend *fe)
10528c2ecf20Sopenharmony_ci{
10538c2ecf20Sopenharmony_ci	struct ds3000_state *state = fe->demodulator_priv;
10548c2ecf20Sopenharmony_ci
10558c2ecf20Sopenharmony_ci	if (state->config->set_lock_led)
10568c2ecf20Sopenharmony_ci		state->config->set_lock_led(fe, 0);
10578c2ecf20Sopenharmony_ci
10588c2ecf20Sopenharmony_ci	dprintk("%s()\n", __func__);
10598c2ecf20Sopenharmony_ci	return DVBFE_ALGO_HW;
10608c2ecf20Sopenharmony_ci}
10618c2ecf20Sopenharmony_ci
10628c2ecf20Sopenharmony_ci/*
10638c2ecf20Sopenharmony_ci * Initialise or wake up device
10648c2ecf20Sopenharmony_ci *
10658c2ecf20Sopenharmony_ci * Power config will reset and load initial firmware if required
10668c2ecf20Sopenharmony_ci */
10678c2ecf20Sopenharmony_cistatic int ds3000_initfe(struct dvb_frontend *fe)
10688c2ecf20Sopenharmony_ci{
10698c2ecf20Sopenharmony_ci	struct ds3000_state *state = fe->demodulator_priv;
10708c2ecf20Sopenharmony_ci	int ret;
10718c2ecf20Sopenharmony_ci
10728c2ecf20Sopenharmony_ci	dprintk("%s()\n", __func__);
10738c2ecf20Sopenharmony_ci	/* hard reset */
10748c2ecf20Sopenharmony_ci	ds3000_writereg(state, 0x08, 0x01 | ds3000_readreg(state, 0x08));
10758c2ecf20Sopenharmony_ci	msleep(1);
10768c2ecf20Sopenharmony_ci
10778c2ecf20Sopenharmony_ci	/* Load the firmware if required */
10788c2ecf20Sopenharmony_ci	ret = ds3000_firmware_ondemand(fe);
10798c2ecf20Sopenharmony_ci	if (ret != 0) {
10808c2ecf20Sopenharmony_ci		printk(KERN_ERR "%s: Unable initialize firmware\n", __func__);
10818c2ecf20Sopenharmony_ci		return ret;
10828c2ecf20Sopenharmony_ci	}
10838c2ecf20Sopenharmony_ci
10848c2ecf20Sopenharmony_ci	return 0;
10858c2ecf20Sopenharmony_ci}
10868c2ecf20Sopenharmony_ci
10878c2ecf20Sopenharmony_cistatic const struct dvb_frontend_ops ds3000_ops = {
10888c2ecf20Sopenharmony_ci	.delsys = { SYS_DVBS, SYS_DVBS2 },
10898c2ecf20Sopenharmony_ci	.info = {
10908c2ecf20Sopenharmony_ci		.name = "Montage Technology DS3000",
10918c2ecf20Sopenharmony_ci		.frequency_min_hz =  950 * MHz,
10928c2ecf20Sopenharmony_ci		.frequency_max_hz = 2150 * MHz,
10938c2ecf20Sopenharmony_ci		.frequency_stepsize_hz = 1011 * kHz,
10948c2ecf20Sopenharmony_ci		.frequency_tolerance_hz = 5 * MHz,
10958c2ecf20Sopenharmony_ci		.symbol_rate_min = 1000000,
10968c2ecf20Sopenharmony_ci		.symbol_rate_max = 45000000,
10978c2ecf20Sopenharmony_ci		.caps = FE_CAN_INVERSION_AUTO |
10988c2ecf20Sopenharmony_ci			FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | FE_CAN_FEC_3_4 |
10998c2ecf20Sopenharmony_ci			FE_CAN_FEC_4_5 | FE_CAN_FEC_5_6 | FE_CAN_FEC_6_7 |
11008c2ecf20Sopenharmony_ci			FE_CAN_FEC_7_8 | FE_CAN_FEC_AUTO |
11018c2ecf20Sopenharmony_ci			FE_CAN_2G_MODULATION |
11028c2ecf20Sopenharmony_ci			FE_CAN_QPSK | FE_CAN_RECOVER
11038c2ecf20Sopenharmony_ci	},
11048c2ecf20Sopenharmony_ci
11058c2ecf20Sopenharmony_ci	.release = ds3000_release,
11068c2ecf20Sopenharmony_ci
11078c2ecf20Sopenharmony_ci	.init = ds3000_initfe,
11088c2ecf20Sopenharmony_ci	.i2c_gate_ctrl = ds3000_i2c_gate_ctrl,
11098c2ecf20Sopenharmony_ci	.read_status = ds3000_read_status,
11108c2ecf20Sopenharmony_ci	.read_ber = ds3000_read_ber,
11118c2ecf20Sopenharmony_ci	.read_signal_strength = ds3000_read_signal_strength,
11128c2ecf20Sopenharmony_ci	.read_snr = ds3000_read_snr,
11138c2ecf20Sopenharmony_ci	.read_ucblocks = ds3000_read_ucblocks,
11148c2ecf20Sopenharmony_ci	.set_voltage = ds3000_set_voltage,
11158c2ecf20Sopenharmony_ci	.set_tone = ds3000_set_tone,
11168c2ecf20Sopenharmony_ci	.diseqc_send_master_cmd = ds3000_send_diseqc_msg,
11178c2ecf20Sopenharmony_ci	.diseqc_send_burst = ds3000_diseqc_send_burst,
11188c2ecf20Sopenharmony_ci	.get_frontend_algo = ds3000_get_algo,
11198c2ecf20Sopenharmony_ci
11208c2ecf20Sopenharmony_ci	.set_frontend = ds3000_set_frontend,
11218c2ecf20Sopenharmony_ci	.tune = ds3000_tune,
11228c2ecf20Sopenharmony_ci};
11238c2ecf20Sopenharmony_ci
11248c2ecf20Sopenharmony_cimodule_param(debug, int, 0644);
11258c2ecf20Sopenharmony_ciMODULE_PARM_DESC(debug, "Activates frontend debugging (default:0)");
11268c2ecf20Sopenharmony_ci
11278c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("DVB Frontend module for Montage Technology DS3000 hardware");
11288c2ecf20Sopenharmony_ciMODULE_AUTHOR("Konstantin Dimitrov <kosio.dimitrov@gmail.com>");
11298c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL");
11308c2ecf20Sopenharmony_ciMODULE_FIRMWARE(DS3000_DEFAULT_FIRMWARE);
1131