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, ®0xc05))) 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