18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci    Driver for Spase SP8870 demodulator
48c2ecf20Sopenharmony_ci
58c2ecf20Sopenharmony_ci    Copyright (C) 1999 Juergen Peitz
68c2ecf20Sopenharmony_ci
78c2ecf20Sopenharmony_ci
88c2ecf20Sopenharmony_ci*/
98c2ecf20Sopenharmony_ci/*
108c2ecf20Sopenharmony_ci * This driver needs external firmware. Please use the command
118c2ecf20Sopenharmony_ci * "<kerneldir>/scripts/get_dvb_firmware alps_tdlb7" to
128c2ecf20Sopenharmony_ci * download/extract it, and then copy it to /usr/lib/hotplug/firmware
138c2ecf20Sopenharmony_ci * or /lib/firmware (depending on configuration of firmware hotplug).
148c2ecf20Sopenharmony_ci */
158c2ecf20Sopenharmony_ci#define SP8870_DEFAULT_FIRMWARE "dvb-fe-sp8870.fw"
168c2ecf20Sopenharmony_ci
178c2ecf20Sopenharmony_ci#include <linux/init.h>
188c2ecf20Sopenharmony_ci#include <linux/module.h>
198c2ecf20Sopenharmony_ci#include <linux/device.h>
208c2ecf20Sopenharmony_ci#include <linux/firmware.h>
218c2ecf20Sopenharmony_ci#include <linux/delay.h>
228c2ecf20Sopenharmony_ci#include <linux/string.h>
238c2ecf20Sopenharmony_ci#include <linux/slab.h>
248c2ecf20Sopenharmony_ci
258c2ecf20Sopenharmony_ci#include <media/dvb_frontend.h>
268c2ecf20Sopenharmony_ci#include "sp8870.h"
278c2ecf20Sopenharmony_ci
288c2ecf20Sopenharmony_ci
298c2ecf20Sopenharmony_cistruct sp8870_state {
308c2ecf20Sopenharmony_ci
318c2ecf20Sopenharmony_ci	struct i2c_adapter* i2c;
328c2ecf20Sopenharmony_ci
338c2ecf20Sopenharmony_ci	const struct sp8870_config* config;
348c2ecf20Sopenharmony_ci
358c2ecf20Sopenharmony_ci	struct dvb_frontend frontend;
368c2ecf20Sopenharmony_ci
378c2ecf20Sopenharmony_ci	/* demodulator private data */
388c2ecf20Sopenharmony_ci	u8 initialised:1;
398c2ecf20Sopenharmony_ci};
408c2ecf20Sopenharmony_ci
418c2ecf20Sopenharmony_cistatic int debug;
428c2ecf20Sopenharmony_ci#define dprintk(args...) \
438c2ecf20Sopenharmony_ci	do { \
448c2ecf20Sopenharmony_ci		if (debug) printk(KERN_DEBUG "sp8870: " args); \
458c2ecf20Sopenharmony_ci	} while (0)
468c2ecf20Sopenharmony_ci
478c2ecf20Sopenharmony_ci/* firmware size for sp8870 */
488c2ecf20Sopenharmony_ci#define SP8870_FIRMWARE_SIZE 16382
498c2ecf20Sopenharmony_ci
508c2ecf20Sopenharmony_ci/* starting point for firmware in file 'Sc_main.mc' */
518c2ecf20Sopenharmony_ci#define SP8870_FIRMWARE_OFFSET 0x0A
528c2ecf20Sopenharmony_ci
538c2ecf20Sopenharmony_cistatic int sp8870_writereg (struct sp8870_state* state, u16 reg, u16 data)
548c2ecf20Sopenharmony_ci{
558c2ecf20Sopenharmony_ci	u8 buf [] = { reg >> 8, reg & 0xff, data >> 8, data & 0xff };
568c2ecf20Sopenharmony_ci	struct i2c_msg msg = { .addr = state->config->demod_address, .flags = 0, .buf = buf, .len = 4 };
578c2ecf20Sopenharmony_ci	int err;
588c2ecf20Sopenharmony_ci
598c2ecf20Sopenharmony_ci	if ((err = i2c_transfer (state->i2c, &msg, 1)) != 1) {
608c2ecf20Sopenharmony_ci		dprintk ("%s: writereg error (err == %i, reg == 0x%02x, data == 0x%02x)\n", __func__, err, reg, data);
618c2ecf20Sopenharmony_ci		return -EREMOTEIO;
628c2ecf20Sopenharmony_ci	}
638c2ecf20Sopenharmony_ci
648c2ecf20Sopenharmony_ci	return 0;
658c2ecf20Sopenharmony_ci}
668c2ecf20Sopenharmony_ci
678c2ecf20Sopenharmony_cistatic int sp8870_readreg (struct sp8870_state* state, u16 reg)
688c2ecf20Sopenharmony_ci{
698c2ecf20Sopenharmony_ci	int ret;
708c2ecf20Sopenharmony_ci	u8 b0 [] = { reg >> 8 , reg & 0xff };
718c2ecf20Sopenharmony_ci	u8 b1 [] = { 0, 0 };
728c2ecf20Sopenharmony_ci	struct i2c_msg msg [] = { { .addr = state->config->demod_address, .flags = 0, .buf = b0, .len = 2 },
738c2ecf20Sopenharmony_ci			   { .addr = state->config->demod_address, .flags = I2C_M_RD, .buf = b1, .len = 2 } };
748c2ecf20Sopenharmony_ci
758c2ecf20Sopenharmony_ci	ret = i2c_transfer (state->i2c, msg, 2);
768c2ecf20Sopenharmony_ci
778c2ecf20Sopenharmony_ci	if (ret != 2) {
788c2ecf20Sopenharmony_ci		dprintk("%s: readreg error (ret == %i)\n", __func__, ret);
798c2ecf20Sopenharmony_ci		return -1;
808c2ecf20Sopenharmony_ci	}
818c2ecf20Sopenharmony_ci
828c2ecf20Sopenharmony_ci	return (b1[0] << 8 | b1[1]);
838c2ecf20Sopenharmony_ci}
848c2ecf20Sopenharmony_ci
858c2ecf20Sopenharmony_cistatic int sp8870_firmware_upload (struct sp8870_state* state, const struct firmware *fw)
868c2ecf20Sopenharmony_ci{
878c2ecf20Sopenharmony_ci	struct i2c_msg msg;
888c2ecf20Sopenharmony_ci	const char *fw_buf = fw->data;
898c2ecf20Sopenharmony_ci	int fw_pos;
908c2ecf20Sopenharmony_ci	u8 tx_buf[255];
918c2ecf20Sopenharmony_ci	int tx_len;
928c2ecf20Sopenharmony_ci	int err = 0;
938c2ecf20Sopenharmony_ci
948c2ecf20Sopenharmony_ci	dprintk ("%s: ...\n", __func__);
958c2ecf20Sopenharmony_ci
968c2ecf20Sopenharmony_ci	if (fw->size < SP8870_FIRMWARE_SIZE + SP8870_FIRMWARE_OFFSET)
978c2ecf20Sopenharmony_ci		return -EINVAL;
988c2ecf20Sopenharmony_ci
998c2ecf20Sopenharmony_ci	// system controller stop
1008c2ecf20Sopenharmony_ci	sp8870_writereg(state, 0x0F00, 0x0000);
1018c2ecf20Sopenharmony_ci
1028c2ecf20Sopenharmony_ci	// instruction RAM register hiword
1038c2ecf20Sopenharmony_ci	sp8870_writereg(state, 0x8F08, ((SP8870_FIRMWARE_SIZE / 2) & 0xFFFF));
1048c2ecf20Sopenharmony_ci
1058c2ecf20Sopenharmony_ci	// instruction RAM MWR
1068c2ecf20Sopenharmony_ci	sp8870_writereg(state, 0x8F0A, ((SP8870_FIRMWARE_SIZE / 2) >> 16));
1078c2ecf20Sopenharmony_ci
1088c2ecf20Sopenharmony_ci	// do firmware upload
1098c2ecf20Sopenharmony_ci	fw_pos = SP8870_FIRMWARE_OFFSET;
1108c2ecf20Sopenharmony_ci	while (fw_pos < SP8870_FIRMWARE_SIZE + SP8870_FIRMWARE_OFFSET){
1118c2ecf20Sopenharmony_ci		tx_len = (fw_pos <= SP8870_FIRMWARE_SIZE + SP8870_FIRMWARE_OFFSET - 252) ? 252 : SP8870_FIRMWARE_SIZE + SP8870_FIRMWARE_OFFSET - fw_pos;
1128c2ecf20Sopenharmony_ci		// write register 0xCF0A
1138c2ecf20Sopenharmony_ci		tx_buf[0] = 0xCF;
1148c2ecf20Sopenharmony_ci		tx_buf[1] = 0x0A;
1158c2ecf20Sopenharmony_ci		memcpy(&tx_buf[2], fw_buf + fw_pos, tx_len);
1168c2ecf20Sopenharmony_ci		msg.addr = state->config->demod_address;
1178c2ecf20Sopenharmony_ci		msg.flags = 0;
1188c2ecf20Sopenharmony_ci		msg.buf = tx_buf;
1198c2ecf20Sopenharmony_ci		msg.len = tx_len + 2;
1208c2ecf20Sopenharmony_ci		if ((err = i2c_transfer (state->i2c, &msg, 1)) != 1) {
1218c2ecf20Sopenharmony_ci			printk("%s: firmware upload failed!\n", __func__);
1228c2ecf20Sopenharmony_ci			printk ("%s: i2c error (err == %i)\n", __func__, err);
1238c2ecf20Sopenharmony_ci			return err;
1248c2ecf20Sopenharmony_ci		}
1258c2ecf20Sopenharmony_ci		fw_pos += tx_len;
1268c2ecf20Sopenharmony_ci	}
1278c2ecf20Sopenharmony_ci
1288c2ecf20Sopenharmony_ci	dprintk ("%s: done!\n", __func__);
1298c2ecf20Sopenharmony_ci	return 0;
1308c2ecf20Sopenharmony_ci};
1318c2ecf20Sopenharmony_ci
1328c2ecf20Sopenharmony_cistatic void sp8870_microcontroller_stop (struct sp8870_state* state)
1338c2ecf20Sopenharmony_ci{
1348c2ecf20Sopenharmony_ci	sp8870_writereg(state, 0x0F08, 0x000);
1358c2ecf20Sopenharmony_ci	sp8870_writereg(state, 0x0F09, 0x000);
1368c2ecf20Sopenharmony_ci
1378c2ecf20Sopenharmony_ci	// microcontroller STOP
1388c2ecf20Sopenharmony_ci	sp8870_writereg(state, 0x0F00, 0x000);
1398c2ecf20Sopenharmony_ci}
1408c2ecf20Sopenharmony_ci
1418c2ecf20Sopenharmony_cistatic void sp8870_microcontroller_start (struct sp8870_state* state)
1428c2ecf20Sopenharmony_ci{
1438c2ecf20Sopenharmony_ci	sp8870_writereg(state, 0x0F08, 0x000);
1448c2ecf20Sopenharmony_ci	sp8870_writereg(state, 0x0F09, 0x000);
1458c2ecf20Sopenharmony_ci
1468c2ecf20Sopenharmony_ci	// microcontroller START
1478c2ecf20Sopenharmony_ci	sp8870_writereg(state, 0x0F00, 0x001);
1488c2ecf20Sopenharmony_ci	// not documented but if we don't read 0x0D01 out here
1498c2ecf20Sopenharmony_ci	// we don't get a correct data valid signal
1508c2ecf20Sopenharmony_ci	sp8870_readreg(state, 0x0D01);
1518c2ecf20Sopenharmony_ci}
1528c2ecf20Sopenharmony_ci
1538c2ecf20Sopenharmony_cistatic int sp8870_read_data_valid_signal(struct sp8870_state* state)
1548c2ecf20Sopenharmony_ci{
1558c2ecf20Sopenharmony_ci	return (sp8870_readreg(state, 0x0D02) > 0);
1568c2ecf20Sopenharmony_ci}
1578c2ecf20Sopenharmony_ci
1588c2ecf20Sopenharmony_cistatic int configure_reg0xc05 (struct dtv_frontend_properties *p, u16 *reg0xc05)
1598c2ecf20Sopenharmony_ci{
1608c2ecf20Sopenharmony_ci	int known_parameters = 1;
1618c2ecf20Sopenharmony_ci
1628c2ecf20Sopenharmony_ci	*reg0xc05 = 0x000;
1638c2ecf20Sopenharmony_ci
1648c2ecf20Sopenharmony_ci	switch (p->modulation) {
1658c2ecf20Sopenharmony_ci	case QPSK:
1668c2ecf20Sopenharmony_ci		break;
1678c2ecf20Sopenharmony_ci	case QAM_16:
1688c2ecf20Sopenharmony_ci		*reg0xc05 |= (1 << 10);
1698c2ecf20Sopenharmony_ci		break;
1708c2ecf20Sopenharmony_ci	case QAM_64:
1718c2ecf20Sopenharmony_ci		*reg0xc05 |= (2 << 10);
1728c2ecf20Sopenharmony_ci		break;
1738c2ecf20Sopenharmony_ci	case QAM_AUTO:
1748c2ecf20Sopenharmony_ci		known_parameters = 0;
1758c2ecf20Sopenharmony_ci		break;
1768c2ecf20Sopenharmony_ci	default:
1778c2ecf20Sopenharmony_ci		return -EINVAL;
1788c2ecf20Sopenharmony_ci	}
1798c2ecf20Sopenharmony_ci
1808c2ecf20Sopenharmony_ci	switch (p->hierarchy) {
1818c2ecf20Sopenharmony_ci	case HIERARCHY_NONE:
1828c2ecf20Sopenharmony_ci		break;
1838c2ecf20Sopenharmony_ci	case HIERARCHY_1:
1848c2ecf20Sopenharmony_ci		*reg0xc05 |= (1 << 7);
1858c2ecf20Sopenharmony_ci		break;
1868c2ecf20Sopenharmony_ci	case HIERARCHY_2:
1878c2ecf20Sopenharmony_ci		*reg0xc05 |= (2 << 7);
1888c2ecf20Sopenharmony_ci		break;
1898c2ecf20Sopenharmony_ci	case HIERARCHY_4:
1908c2ecf20Sopenharmony_ci		*reg0xc05 |= (3 << 7);
1918c2ecf20Sopenharmony_ci		break;
1928c2ecf20Sopenharmony_ci	case HIERARCHY_AUTO:
1938c2ecf20Sopenharmony_ci		known_parameters = 0;
1948c2ecf20Sopenharmony_ci		break;
1958c2ecf20Sopenharmony_ci	default:
1968c2ecf20Sopenharmony_ci		return -EINVAL;
1978c2ecf20Sopenharmony_ci	}
1988c2ecf20Sopenharmony_ci
1998c2ecf20Sopenharmony_ci	switch (p->code_rate_HP) {
2008c2ecf20Sopenharmony_ci	case FEC_1_2:
2018c2ecf20Sopenharmony_ci		break;
2028c2ecf20Sopenharmony_ci	case FEC_2_3:
2038c2ecf20Sopenharmony_ci		*reg0xc05 |= (1 << 3);
2048c2ecf20Sopenharmony_ci		break;
2058c2ecf20Sopenharmony_ci	case FEC_3_4:
2068c2ecf20Sopenharmony_ci		*reg0xc05 |= (2 << 3);
2078c2ecf20Sopenharmony_ci		break;
2088c2ecf20Sopenharmony_ci	case FEC_5_6:
2098c2ecf20Sopenharmony_ci		*reg0xc05 |= (3 << 3);
2108c2ecf20Sopenharmony_ci		break;
2118c2ecf20Sopenharmony_ci	case FEC_7_8:
2128c2ecf20Sopenharmony_ci		*reg0xc05 |= (4 << 3);
2138c2ecf20Sopenharmony_ci		break;
2148c2ecf20Sopenharmony_ci	case FEC_AUTO:
2158c2ecf20Sopenharmony_ci		known_parameters = 0;
2168c2ecf20Sopenharmony_ci		break;
2178c2ecf20Sopenharmony_ci	default:
2188c2ecf20Sopenharmony_ci		return -EINVAL;
2198c2ecf20Sopenharmony_ci	}
2208c2ecf20Sopenharmony_ci
2218c2ecf20Sopenharmony_ci	if (known_parameters)
2228c2ecf20Sopenharmony_ci		*reg0xc05 |= (2 << 1);	/* use specified parameters */
2238c2ecf20Sopenharmony_ci	else
2248c2ecf20Sopenharmony_ci		*reg0xc05 |= (1 << 1);	/* enable autoprobing */
2258c2ecf20Sopenharmony_ci
2268c2ecf20Sopenharmony_ci	return 0;
2278c2ecf20Sopenharmony_ci}
2288c2ecf20Sopenharmony_ci
2298c2ecf20Sopenharmony_cistatic int sp8870_wake_up(struct sp8870_state* state)
2308c2ecf20Sopenharmony_ci{
2318c2ecf20Sopenharmony_ci	// enable TS output and interface pins
2328c2ecf20Sopenharmony_ci	return sp8870_writereg(state, 0xC18, 0x00D);
2338c2ecf20Sopenharmony_ci}
2348c2ecf20Sopenharmony_ci
2358c2ecf20Sopenharmony_cistatic int sp8870_set_frontend_parameters(struct dvb_frontend *fe)
2368c2ecf20Sopenharmony_ci{
2378c2ecf20Sopenharmony_ci	struct dtv_frontend_properties *p = &fe->dtv_property_cache;
2388c2ecf20Sopenharmony_ci	struct sp8870_state* state = fe->demodulator_priv;
2398c2ecf20Sopenharmony_ci	int  err;
2408c2ecf20Sopenharmony_ci	u16 reg0xc05;
2418c2ecf20Sopenharmony_ci
2428c2ecf20Sopenharmony_ci	if ((err = configure_reg0xc05(p, &reg0xc05)))
2438c2ecf20Sopenharmony_ci		return err;
2448c2ecf20Sopenharmony_ci
2458c2ecf20Sopenharmony_ci	// system controller stop
2468c2ecf20Sopenharmony_ci	sp8870_microcontroller_stop(state);
2478c2ecf20Sopenharmony_ci
2488c2ecf20Sopenharmony_ci	// set tuner parameters
2498c2ecf20Sopenharmony_ci	if (fe->ops.tuner_ops.set_params) {
2508c2ecf20Sopenharmony_ci		fe->ops.tuner_ops.set_params(fe);
2518c2ecf20Sopenharmony_ci		if (fe->ops.i2c_gate_ctrl) fe->ops.i2c_gate_ctrl(fe, 0);
2528c2ecf20Sopenharmony_ci	}
2538c2ecf20Sopenharmony_ci
2548c2ecf20Sopenharmony_ci	// sample rate correction bit [23..17]
2558c2ecf20Sopenharmony_ci	sp8870_writereg(state, 0x0319, 0x000A);
2568c2ecf20Sopenharmony_ci
2578c2ecf20Sopenharmony_ci	// sample rate correction bit [16..0]
2588c2ecf20Sopenharmony_ci	sp8870_writereg(state, 0x031A, 0x0AAB);
2598c2ecf20Sopenharmony_ci
2608c2ecf20Sopenharmony_ci	// integer carrier offset
2618c2ecf20Sopenharmony_ci	sp8870_writereg(state, 0x0309, 0x0400);
2628c2ecf20Sopenharmony_ci
2638c2ecf20Sopenharmony_ci	// fractional carrier offset
2648c2ecf20Sopenharmony_ci	sp8870_writereg(state, 0x030A, 0x0000);
2658c2ecf20Sopenharmony_ci
2668c2ecf20Sopenharmony_ci	// filter for 6/7/8 Mhz channel
2678c2ecf20Sopenharmony_ci	if (p->bandwidth_hz == 6000000)
2688c2ecf20Sopenharmony_ci		sp8870_writereg(state, 0x0311, 0x0002);
2698c2ecf20Sopenharmony_ci	else if (p->bandwidth_hz == 7000000)
2708c2ecf20Sopenharmony_ci		sp8870_writereg(state, 0x0311, 0x0001);
2718c2ecf20Sopenharmony_ci	else
2728c2ecf20Sopenharmony_ci		sp8870_writereg(state, 0x0311, 0x0000);
2738c2ecf20Sopenharmony_ci
2748c2ecf20Sopenharmony_ci	// scan order: 2k first = 0x0000, 8k first = 0x0001
2758c2ecf20Sopenharmony_ci	if (p->transmission_mode == TRANSMISSION_MODE_2K)
2768c2ecf20Sopenharmony_ci		sp8870_writereg(state, 0x0338, 0x0000);
2778c2ecf20Sopenharmony_ci	else
2788c2ecf20Sopenharmony_ci		sp8870_writereg(state, 0x0338, 0x0001);
2798c2ecf20Sopenharmony_ci
2808c2ecf20Sopenharmony_ci	sp8870_writereg(state, 0xc05, reg0xc05);
2818c2ecf20Sopenharmony_ci
2828c2ecf20Sopenharmony_ci	// read status reg in order to clear pending irqs
2838c2ecf20Sopenharmony_ci	err = sp8870_readreg(state, 0x200);
2848c2ecf20Sopenharmony_ci	if (err < 0)
2858c2ecf20Sopenharmony_ci		return err;
2868c2ecf20Sopenharmony_ci
2878c2ecf20Sopenharmony_ci	// system controller start
2888c2ecf20Sopenharmony_ci	sp8870_microcontroller_start(state);
2898c2ecf20Sopenharmony_ci
2908c2ecf20Sopenharmony_ci	return 0;
2918c2ecf20Sopenharmony_ci}
2928c2ecf20Sopenharmony_ci
2938c2ecf20Sopenharmony_cistatic int sp8870_init (struct dvb_frontend* fe)
2948c2ecf20Sopenharmony_ci{
2958c2ecf20Sopenharmony_ci	struct sp8870_state* state = fe->demodulator_priv;
2968c2ecf20Sopenharmony_ci	const struct firmware *fw = NULL;
2978c2ecf20Sopenharmony_ci
2988c2ecf20Sopenharmony_ci	sp8870_wake_up(state);
2998c2ecf20Sopenharmony_ci	if (state->initialised) return 0;
3008c2ecf20Sopenharmony_ci	state->initialised = 1;
3018c2ecf20Sopenharmony_ci
3028c2ecf20Sopenharmony_ci	dprintk ("%s\n", __func__);
3038c2ecf20Sopenharmony_ci
3048c2ecf20Sopenharmony_ci
3058c2ecf20Sopenharmony_ci	/* request the firmware, this will block until someone uploads it */
3068c2ecf20Sopenharmony_ci	printk("sp8870: waiting for firmware upload (%s)...\n", SP8870_DEFAULT_FIRMWARE);
3078c2ecf20Sopenharmony_ci	if (state->config->request_firmware(fe, &fw, SP8870_DEFAULT_FIRMWARE)) {
3088c2ecf20Sopenharmony_ci		printk("sp8870: no firmware upload (timeout or file not found?)\n");
3098c2ecf20Sopenharmony_ci		return -EIO;
3108c2ecf20Sopenharmony_ci	}
3118c2ecf20Sopenharmony_ci
3128c2ecf20Sopenharmony_ci	if (sp8870_firmware_upload(state, fw)) {
3138c2ecf20Sopenharmony_ci		printk("sp8870: writing firmware to device failed\n");
3148c2ecf20Sopenharmony_ci		release_firmware(fw);
3158c2ecf20Sopenharmony_ci		return -EIO;
3168c2ecf20Sopenharmony_ci	}
3178c2ecf20Sopenharmony_ci	release_firmware(fw);
3188c2ecf20Sopenharmony_ci	printk("sp8870: firmware upload complete\n");
3198c2ecf20Sopenharmony_ci
3208c2ecf20Sopenharmony_ci	/* enable TS output and interface pins */
3218c2ecf20Sopenharmony_ci	sp8870_writereg(state, 0xc18, 0x00d);
3228c2ecf20Sopenharmony_ci
3238c2ecf20Sopenharmony_ci	// system controller stop
3248c2ecf20Sopenharmony_ci	sp8870_microcontroller_stop(state);
3258c2ecf20Sopenharmony_ci
3268c2ecf20Sopenharmony_ci	// ADC mode
3278c2ecf20Sopenharmony_ci	sp8870_writereg(state, 0x0301, 0x0003);
3288c2ecf20Sopenharmony_ci
3298c2ecf20Sopenharmony_ci	// Reed Solomon parity bytes passed to output
3308c2ecf20Sopenharmony_ci	sp8870_writereg(state, 0x0C13, 0x0001);
3318c2ecf20Sopenharmony_ci
3328c2ecf20Sopenharmony_ci	// MPEG clock is suppressed if no valid data
3338c2ecf20Sopenharmony_ci	sp8870_writereg(state, 0x0C14, 0x0001);
3348c2ecf20Sopenharmony_ci
3358c2ecf20Sopenharmony_ci	/* bit 0x010: enable data valid signal */
3368c2ecf20Sopenharmony_ci	sp8870_writereg(state, 0x0D00, 0x010);
3378c2ecf20Sopenharmony_ci	sp8870_writereg(state, 0x0D01, 0x000);
3388c2ecf20Sopenharmony_ci
3398c2ecf20Sopenharmony_ci	return 0;
3408c2ecf20Sopenharmony_ci}
3418c2ecf20Sopenharmony_ci
3428c2ecf20Sopenharmony_cistatic int sp8870_read_status(struct dvb_frontend *fe,
3438c2ecf20Sopenharmony_ci			      enum fe_status *fe_status)
3448c2ecf20Sopenharmony_ci{
3458c2ecf20Sopenharmony_ci	struct sp8870_state* state = fe->demodulator_priv;
3468c2ecf20Sopenharmony_ci	int status;
3478c2ecf20Sopenharmony_ci	int signal;
3488c2ecf20Sopenharmony_ci
3498c2ecf20Sopenharmony_ci	*fe_status = 0;
3508c2ecf20Sopenharmony_ci
3518c2ecf20Sopenharmony_ci	status = sp8870_readreg (state, 0x0200);
3528c2ecf20Sopenharmony_ci	if (status < 0)
3538c2ecf20Sopenharmony_ci		return -EIO;
3548c2ecf20Sopenharmony_ci
3558c2ecf20Sopenharmony_ci	signal = sp8870_readreg (state, 0x0303);
3568c2ecf20Sopenharmony_ci	if (signal < 0)
3578c2ecf20Sopenharmony_ci		return -EIO;
3588c2ecf20Sopenharmony_ci
3598c2ecf20Sopenharmony_ci	if (signal > 0x0F)
3608c2ecf20Sopenharmony_ci		*fe_status |= FE_HAS_SIGNAL;
3618c2ecf20Sopenharmony_ci	if (status & 0x08)
3628c2ecf20Sopenharmony_ci		*fe_status |= FE_HAS_SYNC;
3638c2ecf20Sopenharmony_ci	if (status & 0x04)
3648c2ecf20Sopenharmony_ci		*fe_status |= FE_HAS_LOCK | FE_HAS_CARRIER | FE_HAS_VITERBI;
3658c2ecf20Sopenharmony_ci
3668c2ecf20Sopenharmony_ci	return 0;
3678c2ecf20Sopenharmony_ci}
3688c2ecf20Sopenharmony_ci
3698c2ecf20Sopenharmony_cistatic int sp8870_read_ber (struct dvb_frontend* fe, u32 * ber)
3708c2ecf20Sopenharmony_ci{
3718c2ecf20Sopenharmony_ci	struct sp8870_state* state = fe->demodulator_priv;
3728c2ecf20Sopenharmony_ci	int ret;
3738c2ecf20Sopenharmony_ci	u32 tmp;
3748c2ecf20Sopenharmony_ci
3758c2ecf20Sopenharmony_ci	*ber = 0;
3768c2ecf20Sopenharmony_ci
3778c2ecf20Sopenharmony_ci	ret = sp8870_readreg(state, 0xC08);
3788c2ecf20Sopenharmony_ci	if (ret < 0)
3798c2ecf20Sopenharmony_ci		return -EIO;
3808c2ecf20Sopenharmony_ci
3818c2ecf20Sopenharmony_ci	tmp = ret & 0x3F;
3828c2ecf20Sopenharmony_ci
3838c2ecf20Sopenharmony_ci	ret = sp8870_readreg(state, 0xC07);
3848c2ecf20Sopenharmony_ci	if (ret < 0)
3858c2ecf20Sopenharmony_ci		return -EIO;
3868c2ecf20Sopenharmony_ci
3878c2ecf20Sopenharmony_ci	tmp = ret << 6;
3888c2ecf20Sopenharmony_ci	if (tmp >= 0x3FFF0)
3898c2ecf20Sopenharmony_ci		tmp = ~0;
3908c2ecf20Sopenharmony_ci
3918c2ecf20Sopenharmony_ci	*ber = tmp;
3928c2ecf20Sopenharmony_ci
3938c2ecf20Sopenharmony_ci	return 0;
3948c2ecf20Sopenharmony_ci}
3958c2ecf20Sopenharmony_ci
3968c2ecf20Sopenharmony_cistatic int sp8870_read_signal_strength(struct dvb_frontend* fe,  u16 * signal)
3978c2ecf20Sopenharmony_ci{
3988c2ecf20Sopenharmony_ci	struct sp8870_state* state = fe->demodulator_priv;
3998c2ecf20Sopenharmony_ci	int ret;
4008c2ecf20Sopenharmony_ci	u16 tmp;
4018c2ecf20Sopenharmony_ci
4028c2ecf20Sopenharmony_ci	*signal = 0;
4038c2ecf20Sopenharmony_ci
4048c2ecf20Sopenharmony_ci	ret = sp8870_readreg (state, 0x306);
4058c2ecf20Sopenharmony_ci	if (ret < 0)
4068c2ecf20Sopenharmony_ci		return -EIO;
4078c2ecf20Sopenharmony_ci
4088c2ecf20Sopenharmony_ci	tmp = ret << 8;
4098c2ecf20Sopenharmony_ci
4108c2ecf20Sopenharmony_ci	ret = sp8870_readreg (state, 0x303);
4118c2ecf20Sopenharmony_ci	if (ret < 0)
4128c2ecf20Sopenharmony_ci		return -EIO;
4138c2ecf20Sopenharmony_ci
4148c2ecf20Sopenharmony_ci	tmp |= ret;
4158c2ecf20Sopenharmony_ci
4168c2ecf20Sopenharmony_ci	if (tmp)
4178c2ecf20Sopenharmony_ci		*signal = 0xFFFF - tmp;
4188c2ecf20Sopenharmony_ci
4198c2ecf20Sopenharmony_ci	return 0;
4208c2ecf20Sopenharmony_ci}
4218c2ecf20Sopenharmony_ci
4228c2ecf20Sopenharmony_cistatic int sp8870_read_uncorrected_blocks (struct dvb_frontend* fe, u32* ublocks)
4238c2ecf20Sopenharmony_ci{
4248c2ecf20Sopenharmony_ci	struct sp8870_state* state = fe->demodulator_priv;
4258c2ecf20Sopenharmony_ci	int ret;
4268c2ecf20Sopenharmony_ci
4278c2ecf20Sopenharmony_ci	*ublocks = 0;
4288c2ecf20Sopenharmony_ci
4298c2ecf20Sopenharmony_ci	ret = sp8870_readreg(state, 0xC0C);
4308c2ecf20Sopenharmony_ci	if (ret < 0)
4318c2ecf20Sopenharmony_ci		return -EIO;
4328c2ecf20Sopenharmony_ci
4338c2ecf20Sopenharmony_ci	if (ret == 0xFFFF)
4348c2ecf20Sopenharmony_ci		ret = ~0;
4358c2ecf20Sopenharmony_ci
4368c2ecf20Sopenharmony_ci	*ublocks = ret;
4378c2ecf20Sopenharmony_ci
4388c2ecf20Sopenharmony_ci	return 0;
4398c2ecf20Sopenharmony_ci}
4408c2ecf20Sopenharmony_ci
4418c2ecf20Sopenharmony_ci/* number of trials to recover from lockup */
4428c2ecf20Sopenharmony_ci#define MAXTRIALS 5
4438c2ecf20Sopenharmony_ci/* maximum checks for data valid signal */
4448c2ecf20Sopenharmony_ci#define MAXCHECKS 100
4458c2ecf20Sopenharmony_ci
4468c2ecf20Sopenharmony_ci/* only for debugging: counter for detected lockups */
4478c2ecf20Sopenharmony_cistatic int lockups;
4488c2ecf20Sopenharmony_ci/* only for debugging: counter for channel switches */
4498c2ecf20Sopenharmony_cistatic int switches;
4508c2ecf20Sopenharmony_ci
4518c2ecf20Sopenharmony_cistatic int sp8870_set_frontend(struct dvb_frontend *fe)
4528c2ecf20Sopenharmony_ci{
4538c2ecf20Sopenharmony_ci	struct dtv_frontend_properties *p = &fe->dtv_property_cache;
4548c2ecf20Sopenharmony_ci	struct sp8870_state* state = fe->demodulator_priv;
4558c2ecf20Sopenharmony_ci
4568c2ecf20Sopenharmony_ci	/*
4578c2ecf20Sopenharmony_ci	    The firmware of the sp8870 sometimes locks up after setting frontend parameters.
4588c2ecf20Sopenharmony_ci	    We try to detect this by checking the data valid signal.
4598c2ecf20Sopenharmony_ci	    If it is not set after MAXCHECKS we try to recover the lockup by setting
4608c2ecf20Sopenharmony_ci	    the frontend parameters again.
4618c2ecf20Sopenharmony_ci	*/
4628c2ecf20Sopenharmony_ci
4638c2ecf20Sopenharmony_ci	int err = 0;
4648c2ecf20Sopenharmony_ci	int valid = 0;
4658c2ecf20Sopenharmony_ci	int trials = 0;
4668c2ecf20Sopenharmony_ci	int check_count = 0;
4678c2ecf20Sopenharmony_ci
4688c2ecf20Sopenharmony_ci	dprintk("%s: frequency = %i\n", __func__, p->frequency);
4698c2ecf20Sopenharmony_ci
4708c2ecf20Sopenharmony_ci	for (trials = 1; trials <= MAXTRIALS; trials++) {
4718c2ecf20Sopenharmony_ci
4728c2ecf20Sopenharmony_ci		err = sp8870_set_frontend_parameters(fe);
4738c2ecf20Sopenharmony_ci		if (err)
4748c2ecf20Sopenharmony_ci			return err;
4758c2ecf20Sopenharmony_ci
4768c2ecf20Sopenharmony_ci		for (check_count = 0; check_count < MAXCHECKS; check_count++) {
4778c2ecf20Sopenharmony_ci//			valid = ((sp8870_readreg(i2c, 0x0200) & 4) == 0);
4788c2ecf20Sopenharmony_ci			valid = sp8870_read_data_valid_signal(state);
4798c2ecf20Sopenharmony_ci			if (valid) {
4808c2ecf20Sopenharmony_ci				dprintk("%s: delay = %i usec\n",
4818c2ecf20Sopenharmony_ci					__func__, check_count * 10);
4828c2ecf20Sopenharmony_ci				break;
4838c2ecf20Sopenharmony_ci			}
4848c2ecf20Sopenharmony_ci			udelay(10);
4858c2ecf20Sopenharmony_ci		}
4868c2ecf20Sopenharmony_ci		if (valid)
4878c2ecf20Sopenharmony_ci			break;
4888c2ecf20Sopenharmony_ci	}
4898c2ecf20Sopenharmony_ci
4908c2ecf20Sopenharmony_ci	if (!valid) {
4918c2ecf20Sopenharmony_ci		printk("%s: firmware crash!!!!!!\n", __func__);
4928c2ecf20Sopenharmony_ci		return -EIO;
4938c2ecf20Sopenharmony_ci	}
4948c2ecf20Sopenharmony_ci
4958c2ecf20Sopenharmony_ci	if (debug) {
4968c2ecf20Sopenharmony_ci		if (valid) {
4978c2ecf20Sopenharmony_ci			if (trials > 1) {
4988c2ecf20Sopenharmony_ci				printk("%s: firmware lockup!!!\n", __func__);
4998c2ecf20Sopenharmony_ci				printk("%s: recovered after %i trial(s))\n",  __func__, trials - 1);
5008c2ecf20Sopenharmony_ci				lockups++;
5018c2ecf20Sopenharmony_ci			}
5028c2ecf20Sopenharmony_ci		}
5038c2ecf20Sopenharmony_ci		switches++;
5048c2ecf20Sopenharmony_ci		printk("%s: switches = %i lockups = %i\n", __func__, switches, lockups);
5058c2ecf20Sopenharmony_ci	}
5068c2ecf20Sopenharmony_ci
5078c2ecf20Sopenharmony_ci	return 0;
5088c2ecf20Sopenharmony_ci}
5098c2ecf20Sopenharmony_ci
5108c2ecf20Sopenharmony_cistatic int sp8870_sleep(struct dvb_frontend* fe)
5118c2ecf20Sopenharmony_ci{
5128c2ecf20Sopenharmony_ci	struct sp8870_state* state = fe->demodulator_priv;
5138c2ecf20Sopenharmony_ci
5148c2ecf20Sopenharmony_ci	// tristate TS output and disable interface pins
5158c2ecf20Sopenharmony_ci	return sp8870_writereg(state, 0xC18, 0x000);
5168c2ecf20Sopenharmony_ci}
5178c2ecf20Sopenharmony_ci
5188c2ecf20Sopenharmony_cistatic int sp8870_get_tune_settings(struct dvb_frontend* fe, struct dvb_frontend_tune_settings* fesettings)
5198c2ecf20Sopenharmony_ci{
5208c2ecf20Sopenharmony_ci	fesettings->min_delay_ms = 350;
5218c2ecf20Sopenharmony_ci	fesettings->step_size = 0;
5228c2ecf20Sopenharmony_ci	fesettings->max_drift = 0;
5238c2ecf20Sopenharmony_ci	return 0;
5248c2ecf20Sopenharmony_ci}
5258c2ecf20Sopenharmony_ci
5268c2ecf20Sopenharmony_cistatic int sp8870_i2c_gate_ctrl(struct dvb_frontend* fe, int enable)
5278c2ecf20Sopenharmony_ci{
5288c2ecf20Sopenharmony_ci	struct sp8870_state* state = fe->demodulator_priv;
5298c2ecf20Sopenharmony_ci
5308c2ecf20Sopenharmony_ci	if (enable) {
5318c2ecf20Sopenharmony_ci		return sp8870_writereg(state, 0x206, 0x001);
5328c2ecf20Sopenharmony_ci	} else {
5338c2ecf20Sopenharmony_ci		return sp8870_writereg(state, 0x206, 0x000);
5348c2ecf20Sopenharmony_ci	}
5358c2ecf20Sopenharmony_ci}
5368c2ecf20Sopenharmony_ci
5378c2ecf20Sopenharmony_cistatic void sp8870_release(struct dvb_frontend* fe)
5388c2ecf20Sopenharmony_ci{
5398c2ecf20Sopenharmony_ci	struct sp8870_state* state = fe->demodulator_priv;
5408c2ecf20Sopenharmony_ci	kfree(state);
5418c2ecf20Sopenharmony_ci}
5428c2ecf20Sopenharmony_ci
5438c2ecf20Sopenharmony_cistatic const struct dvb_frontend_ops sp8870_ops;
5448c2ecf20Sopenharmony_ci
5458c2ecf20Sopenharmony_cistruct dvb_frontend* sp8870_attach(const struct sp8870_config* config,
5468c2ecf20Sopenharmony_ci				   struct i2c_adapter* i2c)
5478c2ecf20Sopenharmony_ci{
5488c2ecf20Sopenharmony_ci	struct sp8870_state* state = NULL;
5498c2ecf20Sopenharmony_ci
5508c2ecf20Sopenharmony_ci	/* allocate memory for the internal state */
5518c2ecf20Sopenharmony_ci	state = kzalloc(sizeof(struct sp8870_state), GFP_KERNEL);
5528c2ecf20Sopenharmony_ci	if (state == NULL) goto error;
5538c2ecf20Sopenharmony_ci
5548c2ecf20Sopenharmony_ci	/* setup the state */
5558c2ecf20Sopenharmony_ci	state->config = config;
5568c2ecf20Sopenharmony_ci	state->i2c = i2c;
5578c2ecf20Sopenharmony_ci	state->initialised = 0;
5588c2ecf20Sopenharmony_ci
5598c2ecf20Sopenharmony_ci	/* check if the demod is there */
5608c2ecf20Sopenharmony_ci	if (sp8870_readreg(state, 0x0200) < 0) goto error;
5618c2ecf20Sopenharmony_ci
5628c2ecf20Sopenharmony_ci	/* create dvb_frontend */
5638c2ecf20Sopenharmony_ci	memcpy(&state->frontend.ops, &sp8870_ops, sizeof(struct dvb_frontend_ops));
5648c2ecf20Sopenharmony_ci	state->frontend.demodulator_priv = state;
5658c2ecf20Sopenharmony_ci	return &state->frontend;
5668c2ecf20Sopenharmony_ci
5678c2ecf20Sopenharmony_cierror:
5688c2ecf20Sopenharmony_ci	kfree(state);
5698c2ecf20Sopenharmony_ci	return NULL;
5708c2ecf20Sopenharmony_ci}
5718c2ecf20Sopenharmony_ci
5728c2ecf20Sopenharmony_cistatic const struct dvb_frontend_ops sp8870_ops = {
5738c2ecf20Sopenharmony_ci	.delsys = { SYS_DVBT },
5748c2ecf20Sopenharmony_ci	.info = {
5758c2ecf20Sopenharmony_ci		.name			= "Spase SP8870 DVB-T",
5768c2ecf20Sopenharmony_ci		.frequency_min_hz	= 470 * MHz,
5778c2ecf20Sopenharmony_ci		.frequency_max_hz	= 860 * MHz,
5788c2ecf20Sopenharmony_ci		.frequency_stepsize_hz	= 166666,
5798c2ecf20Sopenharmony_ci		.caps			= FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 |
5808c2ecf20Sopenharmony_ci					  FE_CAN_FEC_3_4 | FE_CAN_FEC_5_6 |
5818c2ecf20Sopenharmony_ci					  FE_CAN_FEC_7_8 | FE_CAN_FEC_AUTO |
5828c2ecf20Sopenharmony_ci					  FE_CAN_QPSK | FE_CAN_QAM_16 |
5838c2ecf20Sopenharmony_ci					  FE_CAN_QAM_64 | FE_CAN_QAM_AUTO |
5848c2ecf20Sopenharmony_ci					  FE_CAN_HIERARCHY_AUTO |  FE_CAN_RECOVER
5858c2ecf20Sopenharmony_ci	},
5868c2ecf20Sopenharmony_ci
5878c2ecf20Sopenharmony_ci	.release = sp8870_release,
5888c2ecf20Sopenharmony_ci
5898c2ecf20Sopenharmony_ci	.init = sp8870_init,
5908c2ecf20Sopenharmony_ci	.sleep = sp8870_sleep,
5918c2ecf20Sopenharmony_ci	.i2c_gate_ctrl = sp8870_i2c_gate_ctrl,
5928c2ecf20Sopenharmony_ci
5938c2ecf20Sopenharmony_ci	.set_frontend = sp8870_set_frontend,
5948c2ecf20Sopenharmony_ci	.get_tune_settings = sp8870_get_tune_settings,
5958c2ecf20Sopenharmony_ci
5968c2ecf20Sopenharmony_ci	.read_status = sp8870_read_status,
5978c2ecf20Sopenharmony_ci	.read_ber = sp8870_read_ber,
5988c2ecf20Sopenharmony_ci	.read_signal_strength = sp8870_read_signal_strength,
5998c2ecf20Sopenharmony_ci	.read_ucblocks = sp8870_read_uncorrected_blocks,
6008c2ecf20Sopenharmony_ci};
6018c2ecf20Sopenharmony_ci
6028c2ecf20Sopenharmony_cimodule_param(debug, int, 0644);
6038c2ecf20Sopenharmony_ciMODULE_PARM_DESC(debug, "Turn on/off frontend debugging (default:off).");
6048c2ecf20Sopenharmony_ci
6058c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Spase SP8870 DVB-T Demodulator driver");
6068c2ecf20Sopenharmony_ciMODULE_AUTHOR("Juergen Peitz");
6078c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL");
6088c2ecf20Sopenharmony_ci
6098c2ecf20Sopenharmony_ciEXPORT_SYMBOL(sp8870_attach);
610