18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Driver for Zarlink DVB-T MT352 demodulator 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Written by Holger Waechtler <holger@qanu.de> 68c2ecf20Sopenharmony_ci * and Daniel Mack <daniel@qanu.de> 78c2ecf20Sopenharmony_ci * 88c2ecf20Sopenharmony_ci * AVerMedia AVerTV DVB-T 771 support by 98c2ecf20Sopenharmony_ci * Wolfram Joost <dbox2@frokaschwei.de> 108c2ecf20Sopenharmony_ci * 118c2ecf20Sopenharmony_ci * Support for Samsung TDTC9251DH01C(M) tuner 128c2ecf20Sopenharmony_ci * Copyright (C) 2004 Antonio Mancuso <antonio.mancuso@digitaltelevision.it> 138c2ecf20Sopenharmony_ci * Amauri Celani <acelani@essegi.net> 148c2ecf20Sopenharmony_ci * 158c2ecf20Sopenharmony_ci * DVICO FusionHDTV DVB-T1 and DVICO FusionHDTV DVB-T Lite support by 168c2ecf20Sopenharmony_ci * Christopher Pascoe <c.pascoe@itee.uq.edu.au> 178c2ecf20Sopenharmony_ci */ 188c2ecf20Sopenharmony_ci 198c2ecf20Sopenharmony_ci#include <linux/kernel.h> 208c2ecf20Sopenharmony_ci#include <linux/module.h> 218c2ecf20Sopenharmony_ci#include <linux/init.h> 228c2ecf20Sopenharmony_ci#include <linux/delay.h> 238c2ecf20Sopenharmony_ci#include <linux/string.h> 248c2ecf20Sopenharmony_ci#include <linux/slab.h> 258c2ecf20Sopenharmony_ci 268c2ecf20Sopenharmony_ci#include <media/dvb_frontend.h> 278c2ecf20Sopenharmony_ci#include "mt352_priv.h" 288c2ecf20Sopenharmony_ci#include "mt352.h" 298c2ecf20Sopenharmony_ci 308c2ecf20Sopenharmony_cistruct mt352_state { 318c2ecf20Sopenharmony_ci struct i2c_adapter* i2c; 328c2ecf20Sopenharmony_ci struct dvb_frontend frontend; 338c2ecf20Sopenharmony_ci 348c2ecf20Sopenharmony_ci /* configuration settings */ 358c2ecf20Sopenharmony_ci struct mt352_config config; 368c2ecf20Sopenharmony_ci}; 378c2ecf20Sopenharmony_ci 388c2ecf20Sopenharmony_cistatic int debug; 398c2ecf20Sopenharmony_ci#define dprintk(args...) \ 408c2ecf20Sopenharmony_ci do { \ 418c2ecf20Sopenharmony_ci if (debug) printk(KERN_DEBUG "mt352: " args); \ 428c2ecf20Sopenharmony_ci } while (0) 438c2ecf20Sopenharmony_ci 448c2ecf20Sopenharmony_cistatic int mt352_single_write(struct dvb_frontend *fe, u8 reg, u8 val) 458c2ecf20Sopenharmony_ci{ 468c2ecf20Sopenharmony_ci struct mt352_state* state = fe->demodulator_priv; 478c2ecf20Sopenharmony_ci u8 buf[2] = { reg, val }; 488c2ecf20Sopenharmony_ci struct i2c_msg msg = { .addr = state->config.demod_address, .flags = 0, 498c2ecf20Sopenharmony_ci .buf = buf, .len = 2 }; 508c2ecf20Sopenharmony_ci int err = i2c_transfer(state->i2c, &msg, 1); 518c2ecf20Sopenharmony_ci if (err != 1) { 528c2ecf20Sopenharmony_ci printk("mt352_write() to reg %x failed (err = %d)!\n", reg, err); 538c2ecf20Sopenharmony_ci return err; 548c2ecf20Sopenharmony_ci } 558c2ecf20Sopenharmony_ci return 0; 568c2ecf20Sopenharmony_ci} 578c2ecf20Sopenharmony_ci 588c2ecf20Sopenharmony_cistatic int _mt352_write(struct dvb_frontend* fe, const u8 ibuf[], int ilen) 598c2ecf20Sopenharmony_ci{ 608c2ecf20Sopenharmony_ci int err,i; 618c2ecf20Sopenharmony_ci for (i=0; i < ilen-1; i++) 628c2ecf20Sopenharmony_ci if ((err = mt352_single_write(fe,ibuf[0]+i,ibuf[i+1]))) 638c2ecf20Sopenharmony_ci return err; 648c2ecf20Sopenharmony_ci 658c2ecf20Sopenharmony_ci return 0; 668c2ecf20Sopenharmony_ci} 678c2ecf20Sopenharmony_ci 688c2ecf20Sopenharmony_cistatic int mt352_read_register(struct mt352_state* state, u8 reg) 698c2ecf20Sopenharmony_ci{ 708c2ecf20Sopenharmony_ci int ret; 718c2ecf20Sopenharmony_ci u8 b0 [] = { reg }; 728c2ecf20Sopenharmony_ci u8 b1 [] = { 0 }; 738c2ecf20Sopenharmony_ci struct i2c_msg msg [] = { { .addr = state->config.demod_address, 748c2ecf20Sopenharmony_ci .flags = 0, 758c2ecf20Sopenharmony_ci .buf = b0, .len = 1 }, 768c2ecf20Sopenharmony_ci { .addr = state->config.demod_address, 778c2ecf20Sopenharmony_ci .flags = I2C_M_RD, 788c2ecf20Sopenharmony_ci .buf = b1, .len = 1 } }; 798c2ecf20Sopenharmony_ci 808c2ecf20Sopenharmony_ci ret = i2c_transfer(state->i2c, msg, 2); 818c2ecf20Sopenharmony_ci 828c2ecf20Sopenharmony_ci if (ret != 2) { 838c2ecf20Sopenharmony_ci printk("%s: readreg error (reg=%d, ret==%i)\n", 848c2ecf20Sopenharmony_ci __func__, reg, ret); 858c2ecf20Sopenharmony_ci return ret; 868c2ecf20Sopenharmony_ci } 878c2ecf20Sopenharmony_ci 888c2ecf20Sopenharmony_ci return b1[0]; 898c2ecf20Sopenharmony_ci} 908c2ecf20Sopenharmony_ci 918c2ecf20Sopenharmony_cistatic int mt352_sleep(struct dvb_frontend* fe) 928c2ecf20Sopenharmony_ci{ 938c2ecf20Sopenharmony_ci static u8 mt352_softdown[] = { CLOCK_CTL, 0x20, 0x08 }; 948c2ecf20Sopenharmony_ci 958c2ecf20Sopenharmony_ci _mt352_write(fe, mt352_softdown, sizeof(mt352_softdown)); 968c2ecf20Sopenharmony_ci return 0; 978c2ecf20Sopenharmony_ci} 988c2ecf20Sopenharmony_ci 998c2ecf20Sopenharmony_cistatic void mt352_calc_nominal_rate(struct mt352_state* state, 1008c2ecf20Sopenharmony_ci u32 bandwidth, 1018c2ecf20Sopenharmony_ci unsigned char *buf) 1028c2ecf20Sopenharmony_ci{ 1038c2ecf20Sopenharmony_ci u32 adc_clock = 20480; /* 20.340 MHz */ 1048c2ecf20Sopenharmony_ci u32 bw,value; 1058c2ecf20Sopenharmony_ci 1068c2ecf20Sopenharmony_ci switch (bandwidth) { 1078c2ecf20Sopenharmony_ci case 6000000: 1088c2ecf20Sopenharmony_ci bw = 6; 1098c2ecf20Sopenharmony_ci break; 1108c2ecf20Sopenharmony_ci case 7000000: 1118c2ecf20Sopenharmony_ci bw = 7; 1128c2ecf20Sopenharmony_ci break; 1138c2ecf20Sopenharmony_ci case 8000000: 1148c2ecf20Sopenharmony_ci default: 1158c2ecf20Sopenharmony_ci bw = 8; 1168c2ecf20Sopenharmony_ci break; 1178c2ecf20Sopenharmony_ci } 1188c2ecf20Sopenharmony_ci if (state->config.adc_clock) 1198c2ecf20Sopenharmony_ci adc_clock = state->config.adc_clock; 1208c2ecf20Sopenharmony_ci 1218c2ecf20Sopenharmony_ci value = 64 * bw * (1<<16) / (7 * 8); 1228c2ecf20Sopenharmony_ci value = value * 1000 / adc_clock; 1238c2ecf20Sopenharmony_ci dprintk("%s: bw %d, adc_clock %d => 0x%x\n", 1248c2ecf20Sopenharmony_ci __func__, bw, adc_clock, value); 1258c2ecf20Sopenharmony_ci buf[0] = msb(value); 1268c2ecf20Sopenharmony_ci buf[1] = lsb(value); 1278c2ecf20Sopenharmony_ci} 1288c2ecf20Sopenharmony_ci 1298c2ecf20Sopenharmony_cistatic void mt352_calc_input_freq(struct mt352_state* state, 1308c2ecf20Sopenharmony_ci unsigned char *buf) 1318c2ecf20Sopenharmony_ci{ 1328c2ecf20Sopenharmony_ci int adc_clock = 20480; /* 20.480000 MHz */ 1338c2ecf20Sopenharmony_ci int if2 = 36167; /* 36.166667 MHz */ 1348c2ecf20Sopenharmony_ci int ife,value; 1358c2ecf20Sopenharmony_ci 1368c2ecf20Sopenharmony_ci if (state->config.adc_clock) 1378c2ecf20Sopenharmony_ci adc_clock = state->config.adc_clock; 1388c2ecf20Sopenharmony_ci if (state->config.if2) 1398c2ecf20Sopenharmony_ci if2 = state->config.if2; 1408c2ecf20Sopenharmony_ci 1418c2ecf20Sopenharmony_ci if (adc_clock >= if2 * 2) 1428c2ecf20Sopenharmony_ci ife = if2; 1438c2ecf20Sopenharmony_ci else { 1448c2ecf20Sopenharmony_ci ife = adc_clock - (if2 % adc_clock); 1458c2ecf20Sopenharmony_ci if (ife > adc_clock / 2) 1468c2ecf20Sopenharmony_ci ife = adc_clock - ife; 1478c2ecf20Sopenharmony_ci } 1488c2ecf20Sopenharmony_ci value = -16374 * ife / adc_clock; 1498c2ecf20Sopenharmony_ci dprintk("%s: if2 %d, ife %d, adc_clock %d => %d / 0x%x\n", 1508c2ecf20Sopenharmony_ci __func__, if2, ife, adc_clock, value, value & 0x3fff); 1518c2ecf20Sopenharmony_ci buf[0] = msb(value); 1528c2ecf20Sopenharmony_ci buf[1] = lsb(value); 1538c2ecf20Sopenharmony_ci} 1548c2ecf20Sopenharmony_ci 1558c2ecf20Sopenharmony_cistatic int mt352_set_parameters(struct dvb_frontend *fe) 1568c2ecf20Sopenharmony_ci{ 1578c2ecf20Sopenharmony_ci struct dtv_frontend_properties *op = &fe->dtv_property_cache; 1588c2ecf20Sopenharmony_ci struct mt352_state* state = fe->demodulator_priv; 1598c2ecf20Sopenharmony_ci unsigned char buf[13]; 1608c2ecf20Sopenharmony_ci static unsigned char tuner_go[] = { 0x5d, 0x01 }; 1618c2ecf20Sopenharmony_ci static unsigned char fsm_go[] = { 0x5e, 0x01 }; 1628c2ecf20Sopenharmony_ci unsigned int tps = 0; 1638c2ecf20Sopenharmony_ci 1648c2ecf20Sopenharmony_ci switch (op->code_rate_HP) { 1658c2ecf20Sopenharmony_ci case FEC_2_3: 1668c2ecf20Sopenharmony_ci tps |= (1 << 7); 1678c2ecf20Sopenharmony_ci break; 1688c2ecf20Sopenharmony_ci case FEC_3_4: 1698c2ecf20Sopenharmony_ci tps |= (2 << 7); 1708c2ecf20Sopenharmony_ci break; 1718c2ecf20Sopenharmony_ci case FEC_5_6: 1728c2ecf20Sopenharmony_ci tps |= (3 << 7); 1738c2ecf20Sopenharmony_ci break; 1748c2ecf20Sopenharmony_ci case FEC_7_8: 1758c2ecf20Sopenharmony_ci tps |= (4 << 7); 1768c2ecf20Sopenharmony_ci break; 1778c2ecf20Sopenharmony_ci case FEC_1_2: 1788c2ecf20Sopenharmony_ci case FEC_AUTO: 1798c2ecf20Sopenharmony_ci break; 1808c2ecf20Sopenharmony_ci default: 1818c2ecf20Sopenharmony_ci return -EINVAL; 1828c2ecf20Sopenharmony_ci } 1838c2ecf20Sopenharmony_ci 1848c2ecf20Sopenharmony_ci switch (op->code_rate_LP) { 1858c2ecf20Sopenharmony_ci case FEC_2_3: 1868c2ecf20Sopenharmony_ci tps |= (1 << 4); 1878c2ecf20Sopenharmony_ci break; 1888c2ecf20Sopenharmony_ci case FEC_3_4: 1898c2ecf20Sopenharmony_ci tps |= (2 << 4); 1908c2ecf20Sopenharmony_ci break; 1918c2ecf20Sopenharmony_ci case FEC_5_6: 1928c2ecf20Sopenharmony_ci tps |= (3 << 4); 1938c2ecf20Sopenharmony_ci break; 1948c2ecf20Sopenharmony_ci case FEC_7_8: 1958c2ecf20Sopenharmony_ci tps |= (4 << 4); 1968c2ecf20Sopenharmony_ci break; 1978c2ecf20Sopenharmony_ci case FEC_1_2: 1988c2ecf20Sopenharmony_ci case FEC_AUTO: 1998c2ecf20Sopenharmony_ci break; 2008c2ecf20Sopenharmony_ci case FEC_NONE: 2018c2ecf20Sopenharmony_ci if (op->hierarchy == HIERARCHY_AUTO || 2028c2ecf20Sopenharmony_ci op->hierarchy == HIERARCHY_NONE) 2038c2ecf20Sopenharmony_ci break; 2048c2ecf20Sopenharmony_ci fallthrough; 2058c2ecf20Sopenharmony_ci default: 2068c2ecf20Sopenharmony_ci return -EINVAL; 2078c2ecf20Sopenharmony_ci } 2088c2ecf20Sopenharmony_ci 2098c2ecf20Sopenharmony_ci switch (op->modulation) { 2108c2ecf20Sopenharmony_ci case QPSK: 2118c2ecf20Sopenharmony_ci break; 2128c2ecf20Sopenharmony_ci case QAM_AUTO: 2138c2ecf20Sopenharmony_ci case QAM_16: 2148c2ecf20Sopenharmony_ci tps |= (1 << 13); 2158c2ecf20Sopenharmony_ci break; 2168c2ecf20Sopenharmony_ci case QAM_64: 2178c2ecf20Sopenharmony_ci tps |= (2 << 13); 2188c2ecf20Sopenharmony_ci break; 2198c2ecf20Sopenharmony_ci default: 2208c2ecf20Sopenharmony_ci return -EINVAL; 2218c2ecf20Sopenharmony_ci } 2228c2ecf20Sopenharmony_ci 2238c2ecf20Sopenharmony_ci switch (op->transmission_mode) { 2248c2ecf20Sopenharmony_ci case TRANSMISSION_MODE_2K: 2258c2ecf20Sopenharmony_ci case TRANSMISSION_MODE_AUTO: 2268c2ecf20Sopenharmony_ci break; 2278c2ecf20Sopenharmony_ci case TRANSMISSION_MODE_8K: 2288c2ecf20Sopenharmony_ci tps |= (1 << 0); 2298c2ecf20Sopenharmony_ci break; 2308c2ecf20Sopenharmony_ci default: 2318c2ecf20Sopenharmony_ci return -EINVAL; 2328c2ecf20Sopenharmony_ci } 2338c2ecf20Sopenharmony_ci 2348c2ecf20Sopenharmony_ci switch (op->guard_interval) { 2358c2ecf20Sopenharmony_ci case GUARD_INTERVAL_1_32: 2368c2ecf20Sopenharmony_ci case GUARD_INTERVAL_AUTO: 2378c2ecf20Sopenharmony_ci break; 2388c2ecf20Sopenharmony_ci case GUARD_INTERVAL_1_16: 2398c2ecf20Sopenharmony_ci tps |= (1 << 2); 2408c2ecf20Sopenharmony_ci break; 2418c2ecf20Sopenharmony_ci case GUARD_INTERVAL_1_8: 2428c2ecf20Sopenharmony_ci tps |= (2 << 2); 2438c2ecf20Sopenharmony_ci break; 2448c2ecf20Sopenharmony_ci case GUARD_INTERVAL_1_4: 2458c2ecf20Sopenharmony_ci tps |= (3 << 2); 2468c2ecf20Sopenharmony_ci break; 2478c2ecf20Sopenharmony_ci default: 2488c2ecf20Sopenharmony_ci return -EINVAL; 2498c2ecf20Sopenharmony_ci } 2508c2ecf20Sopenharmony_ci 2518c2ecf20Sopenharmony_ci switch (op->hierarchy) { 2528c2ecf20Sopenharmony_ci case HIERARCHY_AUTO: 2538c2ecf20Sopenharmony_ci case HIERARCHY_NONE: 2548c2ecf20Sopenharmony_ci break; 2558c2ecf20Sopenharmony_ci case HIERARCHY_1: 2568c2ecf20Sopenharmony_ci tps |= (1 << 10); 2578c2ecf20Sopenharmony_ci break; 2588c2ecf20Sopenharmony_ci case HIERARCHY_2: 2598c2ecf20Sopenharmony_ci tps |= (2 << 10); 2608c2ecf20Sopenharmony_ci break; 2618c2ecf20Sopenharmony_ci case HIERARCHY_4: 2628c2ecf20Sopenharmony_ci tps |= (3 << 10); 2638c2ecf20Sopenharmony_ci break; 2648c2ecf20Sopenharmony_ci default: 2658c2ecf20Sopenharmony_ci return -EINVAL; 2668c2ecf20Sopenharmony_ci } 2678c2ecf20Sopenharmony_ci 2688c2ecf20Sopenharmony_ci 2698c2ecf20Sopenharmony_ci buf[0] = TPS_GIVEN_1; /* TPS_GIVEN_1 and following registers */ 2708c2ecf20Sopenharmony_ci 2718c2ecf20Sopenharmony_ci buf[1] = msb(tps); /* TPS_GIVEN_(1|0) */ 2728c2ecf20Sopenharmony_ci buf[2] = lsb(tps); 2738c2ecf20Sopenharmony_ci 2748c2ecf20Sopenharmony_ci buf[3] = 0x50; // old 2758c2ecf20Sopenharmony_ci// buf[3] = 0xf4; // pinnacle 2768c2ecf20Sopenharmony_ci 2778c2ecf20Sopenharmony_ci mt352_calc_nominal_rate(state, op->bandwidth_hz, buf+4); 2788c2ecf20Sopenharmony_ci mt352_calc_input_freq(state, buf+6); 2798c2ecf20Sopenharmony_ci 2808c2ecf20Sopenharmony_ci if (state->config.no_tuner) { 2818c2ecf20Sopenharmony_ci if (fe->ops.tuner_ops.set_params) { 2828c2ecf20Sopenharmony_ci fe->ops.tuner_ops.set_params(fe); 2838c2ecf20Sopenharmony_ci if (fe->ops.i2c_gate_ctrl) 2848c2ecf20Sopenharmony_ci fe->ops.i2c_gate_ctrl(fe, 0); 2858c2ecf20Sopenharmony_ci } 2868c2ecf20Sopenharmony_ci 2878c2ecf20Sopenharmony_ci _mt352_write(fe, buf, 8); 2888c2ecf20Sopenharmony_ci _mt352_write(fe, fsm_go, 2); 2898c2ecf20Sopenharmony_ci } else { 2908c2ecf20Sopenharmony_ci if (fe->ops.tuner_ops.calc_regs) { 2918c2ecf20Sopenharmony_ci fe->ops.tuner_ops.calc_regs(fe, buf+8, 5); 2928c2ecf20Sopenharmony_ci buf[8] <<= 1; 2938c2ecf20Sopenharmony_ci _mt352_write(fe, buf, sizeof(buf)); 2948c2ecf20Sopenharmony_ci _mt352_write(fe, tuner_go, 2); 2958c2ecf20Sopenharmony_ci } 2968c2ecf20Sopenharmony_ci } 2978c2ecf20Sopenharmony_ci 2988c2ecf20Sopenharmony_ci return 0; 2998c2ecf20Sopenharmony_ci} 3008c2ecf20Sopenharmony_ci 3018c2ecf20Sopenharmony_cistatic int mt352_get_parameters(struct dvb_frontend* fe, 3028c2ecf20Sopenharmony_ci struct dtv_frontend_properties *op) 3038c2ecf20Sopenharmony_ci{ 3048c2ecf20Sopenharmony_ci struct mt352_state* state = fe->demodulator_priv; 3058c2ecf20Sopenharmony_ci u16 tps; 3068c2ecf20Sopenharmony_ci u16 div; 3078c2ecf20Sopenharmony_ci u8 trl; 3088c2ecf20Sopenharmony_ci static const u8 tps_fec_to_api[8] = 3098c2ecf20Sopenharmony_ci { 3108c2ecf20Sopenharmony_ci FEC_1_2, 3118c2ecf20Sopenharmony_ci FEC_2_3, 3128c2ecf20Sopenharmony_ci FEC_3_4, 3138c2ecf20Sopenharmony_ci FEC_5_6, 3148c2ecf20Sopenharmony_ci FEC_7_8, 3158c2ecf20Sopenharmony_ci FEC_AUTO, 3168c2ecf20Sopenharmony_ci FEC_AUTO, 3178c2ecf20Sopenharmony_ci FEC_AUTO 3188c2ecf20Sopenharmony_ci }; 3198c2ecf20Sopenharmony_ci 3208c2ecf20Sopenharmony_ci if ( (mt352_read_register(state,0x00) & 0xC0) != 0xC0 ) 3218c2ecf20Sopenharmony_ci return -EINVAL; 3228c2ecf20Sopenharmony_ci 3238c2ecf20Sopenharmony_ci /* Use TPS_RECEIVED-registers, not the TPS_CURRENT-registers because 3248c2ecf20Sopenharmony_ci * the mt352 sometimes works with the wrong parameters 3258c2ecf20Sopenharmony_ci */ 3268c2ecf20Sopenharmony_ci tps = (mt352_read_register(state, TPS_RECEIVED_1) << 8) | mt352_read_register(state, TPS_RECEIVED_0); 3278c2ecf20Sopenharmony_ci div = (mt352_read_register(state, CHAN_START_1) << 8) | mt352_read_register(state, CHAN_START_0); 3288c2ecf20Sopenharmony_ci trl = mt352_read_register(state, TRL_NOMINAL_RATE_1); 3298c2ecf20Sopenharmony_ci 3308c2ecf20Sopenharmony_ci op->code_rate_HP = tps_fec_to_api[(tps >> 7) & 7]; 3318c2ecf20Sopenharmony_ci op->code_rate_LP = tps_fec_to_api[(tps >> 4) & 7]; 3328c2ecf20Sopenharmony_ci 3338c2ecf20Sopenharmony_ci switch ( (tps >> 13) & 3) 3348c2ecf20Sopenharmony_ci { 3358c2ecf20Sopenharmony_ci case 0: 3368c2ecf20Sopenharmony_ci op->modulation = QPSK; 3378c2ecf20Sopenharmony_ci break; 3388c2ecf20Sopenharmony_ci case 1: 3398c2ecf20Sopenharmony_ci op->modulation = QAM_16; 3408c2ecf20Sopenharmony_ci break; 3418c2ecf20Sopenharmony_ci case 2: 3428c2ecf20Sopenharmony_ci op->modulation = QAM_64; 3438c2ecf20Sopenharmony_ci break; 3448c2ecf20Sopenharmony_ci default: 3458c2ecf20Sopenharmony_ci op->modulation = QAM_AUTO; 3468c2ecf20Sopenharmony_ci break; 3478c2ecf20Sopenharmony_ci } 3488c2ecf20Sopenharmony_ci 3498c2ecf20Sopenharmony_ci op->transmission_mode = (tps & 0x01) ? TRANSMISSION_MODE_8K : TRANSMISSION_MODE_2K; 3508c2ecf20Sopenharmony_ci 3518c2ecf20Sopenharmony_ci switch ( (tps >> 2) & 3) 3528c2ecf20Sopenharmony_ci { 3538c2ecf20Sopenharmony_ci case 0: 3548c2ecf20Sopenharmony_ci op->guard_interval = GUARD_INTERVAL_1_32; 3558c2ecf20Sopenharmony_ci break; 3568c2ecf20Sopenharmony_ci case 1: 3578c2ecf20Sopenharmony_ci op->guard_interval = GUARD_INTERVAL_1_16; 3588c2ecf20Sopenharmony_ci break; 3598c2ecf20Sopenharmony_ci case 2: 3608c2ecf20Sopenharmony_ci op->guard_interval = GUARD_INTERVAL_1_8; 3618c2ecf20Sopenharmony_ci break; 3628c2ecf20Sopenharmony_ci case 3: 3638c2ecf20Sopenharmony_ci op->guard_interval = GUARD_INTERVAL_1_4; 3648c2ecf20Sopenharmony_ci break; 3658c2ecf20Sopenharmony_ci default: 3668c2ecf20Sopenharmony_ci op->guard_interval = GUARD_INTERVAL_AUTO; 3678c2ecf20Sopenharmony_ci break; 3688c2ecf20Sopenharmony_ci } 3698c2ecf20Sopenharmony_ci 3708c2ecf20Sopenharmony_ci switch ( (tps >> 10) & 7) 3718c2ecf20Sopenharmony_ci { 3728c2ecf20Sopenharmony_ci case 0: 3738c2ecf20Sopenharmony_ci op->hierarchy = HIERARCHY_NONE; 3748c2ecf20Sopenharmony_ci break; 3758c2ecf20Sopenharmony_ci case 1: 3768c2ecf20Sopenharmony_ci op->hierarchy = HIERARCHY_1; 3778c2ecf20Sopenharmony_ci break; 3788c2ecf20Sopenharmony_ci case 2: 3798c2ecf20Sopenharmony_ci op->hierarchy = HIERARCHY_2; 3808c2ecf20Sopenharmony_ci break; 3818c2ecf20Sopenharmony_ci case 3: 3828c2ecf20Sopenharmony_ci op->hierarchy = HIERARCHY_4; 3838c2ecf20Sopenharmony_ci break; 3848c2ecf20Sopenharmony_ci default: 3858c2ecf20Sopenharmony_ci op->hierarchy = HIERARCHY_AUTO; 3868c2ecf20Sopenharmony_ci break; 3878c2ecf20Sopenharmony_ci } 3888c2ecf20Sopenharmony_ci 3898c2ecf20Sopenharmony_ci op->frequency = (500 * (div - IF_FREQUENCYx6)) / 3 * 1000; 3908c2ecf20Sopenharmony_ci 3918c2ecf20Sopenharmony_ci if (trl == 0x72) 3928c2ecf20Sopenharmony_ci op->bandwidth_hz = 8000000; 3938c2ecf20Sopenharmony_ci else if (trl == 0x64) 3948c2ecf20Sopenharmony_ci op->bandwidth_hz = 7000000; 3958c2ecf20Sopenharmony_ci else 3968c2ecf20Sopenharmony_ci op->bandwidth_hz = 6000000; 3978c2ecf20Sopenharmony_ci 3988c2ecf20Sopenharmony_ci 3998c2ecf20Sopenharmony_ci if (mt352_read_register(state, STATUS_2) & 0x02) 4008c2ecf20Sopenharmony_ci op->inversion = INVERSION_OFF; 4018c2ecf20Sopenharmony_ci else 4028c2ecf20Sopenharmony_ci op->inversion = INVERSION_ON; 4038c2ecf20Sopenharmony_ci 4048c2ecf20Sopenharmony_ci return 0; 4058c2ecf20Sopenharmony_ci} 4068c2ecf20Sopenharmony_ci 4078c2ecf20Sopenharmony_cistatic int mt352_read_status(struct dvb_frontend *fe, enum fe_status *status) 4088c2ecf20Sopenharmony_ci{ 4098c2ecf20Sopenharmony_ci struct mt352_state* state = fe->demodulator_priv; 4108c2ecf20Sopenharmony_ci int s0, s1, s3; 4118c2ecf20Sopenharmony_ci 4128c2ecf20Sopenharmony_ci /* FIXME: 4138c2ecf20Sopenharmony_ci * 4148c2ecf20Sopenharmony_ci * The MT352 design manual from Zarlink states (page 46-47): 4158c2ecf20Sopenharmony_ci * 4168c2ecf20Sopenharmony_ci * Notes about the TUNER_GO register: 4178c2ecf20Sopenharmony_ci * 4188c2ecf20Sopenharmony_ci * If the Read_Tuner_Byte (bit-1) is activated, then the tuner status 4198c2ecf20Sopenharmony_ci * byte is copied from the tuner to the STATUS_3 register and 4208c2ecf20Sopenharmony_ci * completion of the read operation is indicated by bit-5 of the 4218c2ecf20Sopenharmony_ci * INTERRUPT_3 register. 4228c2ecf20Sopenharmony_ci */ 4238c2ecf20Sopenharmony_ci 4248c2ecf20Sopenharmony_ci if ((s0 = mt352_read_register(state, STATUS_0)) < 0) 4258c2ecf20Sopenharmony_ci return -EREMOTEIO; 4268c2ecf20Sopenharmony_ci if ((s1 = mt352_read_register(state, STATUS_1)) < 0) 4278c2ecf20Sopenharmony_ci return -EREMOTEIO; 4288c2ecf20Sopenharmony_ci if ((s3 = mt352_read_register(state, STATUS_3)) < 0) 4298c2ecf20Sopenharmony_ci return -EREMOTEIO; 4308c2ecf20Sopenharmony_ci 4318c2ecf20Sopenharmony_ci *status = 0; 4328c2ecf20Sopenharmony_ci if (s0 & (1 << 4)) 4338c2ecf20Sopenharmony_ci *status |= FE_HAS_CARRIER; 4348c2ecf20Sopenharmony_ci if (s0 & (1 << 1)) 4358c2ecf20Sopenharmony_ci *status |= FE_HAS_VITERBI; 4368c2ecf20Sopenharmony_ci if (s0 & (1 << 5)) 4378c2ecf20Sopenharmony_ci *status |= FE_HAS_LOCK; 4388c2ecf20Sopenharmony_ci if (s1 & (1 << 1)) 4398c2ecf20Sopenharmony_ci *status |= FE_HAS_SYNC; 4408c2ecf20Sopenharmony_ci if (s3 & (1 << 6)) 4418c2ecf20Sopenharmony_ci *status |= FE_HAS_SIGNAL; 4428c2ecf20Sopenharmony_ci 4438c2ecf20Sopenharmony_ci if ((*status & (FE_HAS_CARRIER | FE_HAS_VITERBI | FE_HAS_SYNC)) != 4448c2ecf20Sopenharmony_ci (FE_HAS_CARRIER | FE_HAS_VITERBI | FE_HAS_SYNC)) 4458c2ecf20Sopenharmony_ci *status &= ~FE_HAS_LOCK; 4468c2ecf20Sopenharmony_ci 4478c2ecf20Sopenharmony_ci return 0; 4488c2ecf20Sopenharmony_ci} 4498c2ecf20Sopenharmony_ci 4508c2ecf20Sopenharmony_cistatic int mt352_read_ber(struct dvb_frontend* fe, u32* ber) 4518c2ecf20Sopenharmony_ci{ 4528c2ecf20Sopenharmony_ci struct mt352_state* state = fe->demodulator_priv; 4538c2ecf20Sopenharmony_ci 4548c2ecf20Sopenharmony_ci *ber = (mt352_read_register (state, RS_ERR_CNT_2) << 16) | 4558c2ecf20Sopenharmony_ci (mt352_read_register (state, RS_ERR_CNT_1) << 8) | 4568c2ecf20Sopenharmony_ci (mt352_read_register (state, RS_ERR_CNT_0)); 4578c2ecf20Sopenharmony_ci 4588c2ecf20Sopenharmony_ci return 0; 4598c2ecf20Sopenharmony_ci} 4608c2ecf20Sopenharmony_ci 4618c2ecf20Sopenharmony_cistatic int mt352_read_signal_strength(struct dvb_frontend* fe, u16* strength) 4628c2ecf20Sopenharmony_ci{ 4638c2ecf20Sopenharmony_ci struct mt352_state* state = fe->demodulator_priv; 4648c2ecf20Sopenharmony_ci 4658c2ecf20Sopenharmony_ci /* align the 12 bit AGC gain with the most significant bits */ 4668c2ecf20Sopenharmony_ci u16 signal = ((mt352_read_register(state, AGC_GAIN_1) & 0x0f) << 12) | 4678c2ecf20Sopenharmony_ci (mt352_read_register(state, AGC_GAIN_0) << 4); 4688c2ecf20Sopenharmony_ci 4698c2ecf20Sopenharmony_ci /* inverse of gain is signal strength */ 4708c2ecf20Sopenharmony_ci *strength = ~signal; 4718c2ecf20Sopenharmony_ci return 0; 4728c2ecf20Sopenharmony_ci} 4738c2ecf20Sopenharmony_ci 4748c2ecf20Sopenharmony_cistatic int mt352_read_snr(struct dvb_frontend* fe, u16* snr) 4758c2ecf20Sopenharmony_ci{ 4768c2ecf20Sopenharmony_ci struct mt352_state* state = fe->demodulator_priv; 4778c2ecf20Sopenharmony_ci 4788c2ecf20Sopenharmony_ci u8 _snr = mt352_read_register (state, SNR); 4798c2ecf20Sopenharmony_ci *snr = (_snr << 8) | _snr; 4808c2ecf20Sopenharmony_ci 4818c2ecf20Sopenharmony_ci return 0; 4828c2ecf20Sopenharmony_ci} 4838c2ecf20Sopenharmony_ci 4848c2ecf20Sopenharmony_cistatic int mt352_read_ucblocks(struct dvb_frontend* fe, u32* ucblocks) 4858c2ecf20Sopenharmony_ci{ 4868c2ecf20Sopenharmony_ci struct mt352_state* state = fe->demodulator_priv; 4878c2ecf20Sopenharmony_ci 4888c2ecf20Sopenharmony_ci *ucblocks = (mt352_read_register (state, RS_UBC_1) << 8) | 4898c2ecf20Sopenharmony_ci (mt352_read_register (state, RS_UBC_0)); 4908c2ecf20Sopenharmony_ci 4918c2ecf20Sopenharmony_ci return 0; 4928c2ecf20Sopenharmony_ci} 4938c2ecf20Sopenharmony_ci 4948c2ecf20Sopenharmony_cistatic int mt352_get_tune_settings(struct dvb_frontend* fe, struct dvb_frontend_tune_settings* fe_tune_settings) 4958c2ecf20Sopenharmony_ci{ 4968c2ecf20Sopenharmony_ci fe_tune_settings->min_delay_ms = 800; 4978c2ecf20Sopenharmony_ci fe_tune_settings->step_size = 0; 4988c2ecf20Sopenharmony_ci fe_tune_settings->max_drift = 0; 4998c2ecf20Sopenharmony_ci 5008c2ecf20Sopenharmony_ci return 0; 5018c2ecf20Sopenharmony_ci} 5028c2ecf20Sopenharmony_ci 5038c2ecf20Sopenharmony_cistatic int mt352_init(struct dvb_frontend* fe) 5048c2ecf20Sopenharmony_ci{ 5058c2ecf20Sopenharmony_ci struct mt352_state* state = fe->demodulator_priv; 5068c2ecf20Sopenharmony_ci 5078c2ecf20Sopenharmony_ci static u8 mt352_reset_attach [] = { RESET, 0xC0 }; 5088c2ecf20Sopenharmony_ci 5098c2ecf20Sopenharmony_ci dprintk("%s: hello\n",__func__); 5108c2ecf20Sopenharmony_ci 5118c2ecf20Sopenharmony_ci if ((mt352_read_register(state, CLOCK_CTL) & 0x10) == 0 || 5128c2ecf20Sopenharmony_ci (mt352_read_register(state, CONFIG) & 0x20) == 0) { 5138c2ecf20Sopenharmony_ci 5148c2ecf20Sopenharmony_ci /* Do a "hard" reset */ 5158c2ecf20Sopenharmony_ci _mt352_write(fe, mt352_reset_attach, sizeof(mt352_reset_attach)); 5168c2ecf20Sopenharmony_ci return state->config.demod_init(fe); 5178c2ecf20Sopenharmony_ci } 5188c2ecf20Sopenharmony_ci 5198c2ecf20Sopenharmony_ci return 0; 5208c2ecf20Sopenharmony_ci} 5218c2ecf20Sopenharmony_ci 5228c2ecf20Sopenharmony_cistatic void mt352_release(struct dvb_frontend* fe) 5238c2ecf20Sopenharmony_ci{ 5248c2ecf20Sopenharmony_ci struct mt352_state* state = fe->demodulator_priv; 5258c2ecf20Sopenharmony_ci kfree(state); 5268c2ecf20Sopenharmony_ci} 5278c2ecf20Sopenharmony_ci 5288c2ecf20Sopenharmony_cistatic const struct dvb_frontend_ops mt352_ops; 5298c2ecf20Sopenharmony_ci 5308c2ecf20Sopenharmony_cistruct dvb_frontend* mt352_attach(const struct mt352_config* config, 5318c2ecf20Sopenharmony_ci struct i2c_adapter* i2c) 5328c2ecf20Sopenharmony_ci{ 5338c2ecf20Sopenharmony_ci struct mt352_state* state = NULL; 5348c2ecf20Sopenharmony_ci 5358c2ecf20Sopenharmony_ci /* allocate memory for the internal state */ 5368c2ecf20Sopenharmony_ci state = kzalloc(sizeof(struct mt352_state), GFP_KERNEL); 5378c2ecf20Sopenharmony_ci if (state == NULL) goto error; 5388c2ecf20Sopenharmony_ci 5398c2ecf20Sopenharmony_ci /* setup the state */ 5408c2ecf20Sopenharmony_ci state->i2c = i2c; 5418c2ecf20Sopenharmony_ci memcpy(&state->config,config,sizeof(struct mt352_config)); 5428c2ecf20Sopenharmony_ci 5438c2ecf20Sopenharmony_ci /* check if the demod is there */ 5448c2ecf20Sopenharmony_ci if (mt352_read_register(state, CHIP_ID) != ID_MT352) goto error; 5458c2ecf20Sopenharmony_ci 5468c2ecf20Sopenharmony_ci /* create dvb_frontend */ 5478c2ecf20Sopenharmony_ci memcpy(&state->frontend.ops, &mt352_ops, sizeof(struct dvb_frontend_ops)); 5488c2ecf20Sopenharmony_ci state->frontend.demodulator_priv = state; 5498c2ecf20Sopenharmony_ci return &state->frontend; 5508c2ecf20Sopenharmony_ci 5518c2ecf20Sopenharmony_cierror: 5528c2ecf20Sopenharmony_ci kfree(state); 5538c2ecf20Sopenharmony_ci return NULL; 5548c2ecf20Sopenharmony_ci} 5558c2ecf20Sopenharmony_ci 5568c2ecf20Sopenharmony_cistatic const struct dvb_frontend_ops mt352_ops = { 5578c2ecf20Sopenharmony_ci .delsys = { SYS_DVBT }, 5588c2ecf20Sopenharmony_ci .info = { 5598c2ecf20Sopenharmony_ci .name = "Zarlink MT352 DVB-T", 5608c2ecf20Sopenharmony_ci .frequency_min_hz = 174 * MHz, 5618c2ecf20Sopenharmony_ci .frequency_max_hz = 862 * MHz, 5628c2ecf20Sopenharmony_ci .frequency_stepsize_hz = 166667, 5638c2ecf20Sopenharmony_ci .caps = FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | 5648c2ecf20Sopenharmony_ci FE_CAN_FEC_3_4 | FE_CAN_FEC_5_6 | FE_CAN_FEC_7_8 | 5658c2ecf20Sopenharmony_ci FE_CAN_FEC_AUTO | 5668c2ecf20Sopenharmony_ci FE_CAN_QPSK | FE_CAN_QAM_16 | FE_CAN_QAM_64 | FE_CAN_QAM_AUTO | 5678c2ecf20Sopenharmony_ci FE_CAN_TRANSMISSION_MODE_AUTO | FE_CAN_GUARD_INTERVAL_AUTO | 5688c2ecf20Sopenharmony_ci FE_CAN_HIERARCHY_AUTO | FE_CAN_RECOVER | 5698c2ecf20Sopenharmony_ci FE_CAN_MUTE_TS 5708c2ecf20Sopenharmony_ci }, 5718c2ecf20Sopenharmony_ci 5728c2ecf20Sopenharmony_ci .release = mt352_release, 5738c2ecf20Sopenharmony_ci 5748c2ecf20Sopenharmony_ci .init = mt352_init, 5758c2ecf20Sopenharmony_ci .sleep = mt352_sleep, 5768c2ecf20Sopenharmony_ci .write = _mt352_write, 5778c2ecf20Sopenharmony_ci 5788c2ecf20Sopenharmony_ci .set_frontend = mt352_set_parameters, 5798c2ecf20Sopenharmony_ci .get_frontend = mt352_get_parameters, 5808c2ecf20Sopenharmony_ci .get_tune_settings = mt352_get_tune_settings, 5818c2ecf20Sopenharmony_ci 5828c2ecf20Sopenharmony_ci .read_status = mt352_read_status, 5838c2ecf20Sopenharmony_ci .read_ber = mt352_read_ber, 5848c2ecf20Sopenharmony_ci .read_signal_strength = mt352_read_signal_strength, 5858c2ecf20Sopenharmony_ci .read_snr = mt352_read_snr, 5868c2ecf20Sopenharmony_ci .read_ucblocks = mt352_read_ucblocks, 5878c2ecf20Sopenharmony_ci}; 5888c2ecf20Sopenharmony_ci 5898c2ecf20Sopenharmony_cimodule_param(debug, int, 0644); 5908c2ecf20Sopenharmony_ciMODULE_PARM_DESC(debug, "Turn on/off frontend debugging (default:off)."); 5918c2ecf20Sopenharmony_ci 5928c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Zarlink MT352 DVB-T Demodulator driver"); 5938c2ecf20Sopenharmony_ciMODULE_AUTHOR("Holger Waechtler, Daniel Mack, Antonio Mancuso"); 5948c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 5958c2ecf20Sopenharmony_ci 5968c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(mt352_attach); 597