18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Support for AltoBeam GB20600 (a.k.a DMB-TH) demodulator 48c2ecf20Sopenharmony_ci * ATBM8830, ATBM8831 58c2ecf20Sopenharmony_ci * 68c2ecf20Sopenharmony_ci * Copyright (C) 2009 David T.L. Wong <davidtlwong@gmail.com> 78c2ecf20Sopenharmony_ci */ 88c2ecf20Sopenharmony_ci 98c2ecf20Sopenharmony_ci#include <asm/div64.h> 108c2ecf20Sopenharmony_ci#include <media/dvb_frontend.h> 118c2ecf20Sopenharmony_ci 128c2ecf20Sopenharmony_ci#include "atbm8830.h" 138c2ecf20Sopenharmony_ci#include "atbm8830_priv.h" 148c2ecf20Sopenharmony_ci 158c2ecf20Sopenharmony_ci#define dprintk(args...) \ 168c2ecf20Sopenharmony_ci do { \ 178c2ecf20Sopenharmony_ci if (debug) \ 188c2ecf20Sopenharmony_ci printk(KERN_DEBUG "atbm8830: " args); \ 198c2ecf20Sopenharmony_ci } while (0) 208c2ecf20Sopenharmony_ci 218c2ecf20Sopenharmony_cistatic int debug; 228c2ecf20Sopenharmony_ci 238c2ecf20Sopenharmony_cimodule_param(debug, int, 0644); 248c2ecf20Sopenharmony_ciMODULE_PARM_DESC(debug, "Turn on/off frontend debugging (default:off)."); 258c2ecf20Sopenharmony_ci 268c2ecf20Sopenharmony_cistatic int atbm8830_write_reg(struct atbm_state *priv, u16 reg, u8 data) 278c2ecf20Sopenharmony_ci{ 288c2ecf20Sopenharmony_ci int ret = 0; 298c2ecf20Sopenharmony_ci u8 dev_addr; 308c2ecf20Sopenharmony_ci u8 buf1[] = { reg >> 8, reg & 0xFF }; 318c2ecf20Sopenharmony_ci u8 buf2[] = { data }; 328c2ecf20Sopenharmony_ci struct i2c_msg msg1 = { .flags = 0, .buf = buf1, .len = 2 }; 338c2ecf20Sopenharmony_ci struct i2c_msg msg2 = { .flags = 0, .buf = buf2, .len = 1 }; 348c2ecf20Sopenharmony_ci 358c2ecf20Sopenharmony_ci dev_addr = priv->config->demod_address; 368c2ecf20Sopenharmony_ci msg1.addr = dev_addr; 378c2ecf20Sopenharmony_ci msg2.addr = dev_addr; 388c2ecf20Sopenharmony_ci 398c2ecf20Sopenharmony_ci if (debug >= 2) 408c2ecf20Sopenharmony_ci dprintk("%s: reg=0x%04X, data=0x%02X\n", __func__, reg, data); 418c2ecf20Sopenharmony_ci 428c2ecf20Sopenharmony_ci ret = i2c_transfer(priv->i2c, &msg1, 1); 438c2ecf20Sopenharmony_ci if (ret != 1) 448c2ecf20Sopenharmony_ci return -EIO; 458c2ecf20Sopenharmony_ci 468c2ecf20Sopenharmony_ci ret = i2c_transfer(priv->i2c, &msg2, 1); 478c2ecf20Sopenharmony_ci return (ret != 1) ? -EIO : 0; 488c2ecf20Sopenharmony_ci} 498c2ecf20Sopenharmony_ci 508c2ecf20Sopenharmony_cistatic int atbm8830_read_reg(struct atbm_state *priv, u16 reg, u8 *p_data) 518c2ecf20Sopenharmony_ci{ 528c2ecf20Sopenharmony_ci int ret; 538c2ecf20Sopenharmony_ci u8 dev_addr; 548c2ecf20Sopenharmony_ci 558c2ecf20Sopenharmony_ci u8 buf1[] = { reg >> 8, reg & 0xFF }; 568c2ecf20Sopenharmony_ci u8 buf2[] = { 0 }; 578c2ecf20Sopenharmony_ci struct i2c_msg msg1 = { .flags = 0, .buf = buf1, .len = 2 }; 588c2ecf20Sopenharmony_ci struct i2c_msg msg2 = { .flags = I2C_M_RD, .buf = buf2, .len = 1 }; 598c2ecf20Sopenharmony_ci 608c2ecf20Sopenharmony_ci dev_addr = priv->config->demod_address; 618c2ecf20Sopenharmony_ci msg1.addr = dev_addr; 628c2ecf20Sopenharmony_ci msg2.addr = dev_addr; 638c2ecf20Sopenharmony_ci 648c2ecf20Sopenharmony_ci ret = i2c_transfer(priv->i2c, &msg1, 1); 658c2ecf20Sopenharmony_ci if (ret != 1) { 668c2ecf20Sopenharmony_ci dprintk("%s: error reg=0x%04x, ret=%i\n", __func__, reg, ret); 678c2ecf20Sopenharmony_ci return -EIO; 688c2ecf20Sopenharmony_ci } 698c2ecf20Sopenharmony_ci 708c2ecf20Sopenharmony_ci ret = i2c_transfer(priv->i2c, &msg2, 1); 718c2ecf20Sopenharmony_ci if (ret != 1) 728c2ecf20Sopenharmony_ci return -EIO; 738c2ecf20Sopenharmony_ci 748c2ecf20Sopenharmony_ci *p_data = buf2[0]; 758c2ecf20Sopenharmony_ci if (debug >= 2) 768c2ecf20Sopenharmony_ci dprintk("%s: reg=0x%04X, data=0x%02X\n", 778c2ecf20Sopenharmony_ci __func__, reg, buf2[0]); 788c2ecf20Sopenharmony_ci 798c2ecf20Sopenharmony_ci return 0; 808c2ecf20Sopenharmony_ci} 818c2ecf20Sopenharmony_ci 828c2ecf20Sopenharmony_ci/* Lock register latch so that multi-register read is atomic */ 838c2ecf20Sopenharmony_cistatic inline int atbm8830_reglatch_lock(struct atbm_state *priv, int lock) 848c2ecf20Sopenharmony_ci{ 858c2ecf20Sopenharmony_ci return atbm8830_write_reg(priv, REG_READ_LATCH, lock ? 1 : 0); 868c2ecf20Sopenharmony_ci} 878c2ecf20Sopenharmony_ci 888c2ecf20Sopenharmony_cistatic int set_osc_freq(struct atbm_state *priv, u32 freq /*in kHz*/) 898c2ecf20Sopenharmony_ci{ 908c2ecf20Sopenharmony_ci u32 val; 918c2ecf20Sopenharmony_ci u64 t; 928c2ecf20Sopenharmony_ci 938c2ecf20Sopenharmony_ci /* 0x100000 * freq / 30.4MHz */ 948c2ecf20Sopenharmony_ci t = (u64)0x100000 * freq; 958c2ecf20Sopenharmony_ci do_div(t, 30400); 968c2ecf20Sopenharmony_ci val = t; 978c2ecf20Sopenharmony_ci 988c2ecf20Sopenharmony_ci atbm8830_write_reg(priv, REG_OSC_CLK, val); 998c2ecf20Sopenharmony_ci atbm8830_write_reg(priv, REG_OSC_CLK + 1, val >> 8); 1008c2ecf20Sopenharmony_ci atbm8830_write_reg(priv, REG_OSC_CLK + 2, val >> 16); 1018c2ecf20Sopenharmony_ci 1028c2ecf20Sopenharmony_ci return 0; 1038c2ecf20Sopenharmony_ci} 1048c2ecf20Sopenharmony_ci 1058c2ecf20Sopenharmony_cistatic int set_if_freq(struct atbm_state *priv, u32 freq /*in kHz*/) 1068c2ecf20Sopenharmony_ci{ 1078c2ecf20Sopenharmony_ci 1088c2ecf20Sopenharmony_ci u32 fs = priv->config->osc_clk_freq; 1098c2ecf20Sopenharmony_ci u64 t; 1108c2ecf20Sopenharmony_ci u32 val; 1118c2ecf20Sopenharmony_ci u8 dat; 1128c2ecf20Sopenharmony_ci 1138c2ecf20Sopenharmony_ci if (freq != 0) { 1148c2ecf20Sopenharmony_ci /* 2 * PI * (freq - fs) / fs * (2 ^ 22) */ 1158c2ecf20Sopenharmony_ci t = (u64) 2 * 31416 * (freq - fs); 1168c2ecf20Sopenharmony_ci t <<= 22; 1178c2ecf20Sopenharmony_ci do_div(t, fs); 1188c2ecf20Sopenharmony_ci do_div(t, 1000); 1198c2ecf20Sopenharmony_ci val = t; 1208c2ecf20Sopenharmony_ci 1218c2ecf20Sopenharmony_ci atbm8830_write_reg(priv, REG_TUNER_BASEBAND, 1); 1228c2ecf20Sopenharmony_ci atbm8830_write_reg(priv, REG_IF_FREQ, val); 1238c2ecf20Sopenharmony_ci atbm8830_write_reg(priv, REG_IF_FREQ+1, val >> 8); 1248c2ecf20Sopenharmony_ci atbm8830_write_reg(priv, REG_IF_FREQ+2, val >> 16); 1258c2ecf20Sopenharmony_ci 1268c2ecf20Sopenharmony_ci atbm8830_read_reg(priv, REG_ADC_CONFIG, &dat); 1278c2ecf20Sopenharmony_ci dat &= 0xFC; 1288c2ecf20Sopenharmony_ci atbm8830_write_reg(priv, REG_ADC_CONFIG, dat); 1298c2ecf20Sopenharmony_ci } else { 1308c2ecf20Sopenharmony_ci /* Zero IF */ 1318c2ecf20Sopenharmony_ci atbm8830_write_reg(priv, REG_TUNER_BASEBAND, 0); 1328c2ecf20Sopenharmony_ci 1338c2ecf20Sopenharmony_ci atbm8830_read_reg(priv, REG_ADC_CONFIG, &dat); 1348c2ecf20Sopenharmony_ci dat &= 0xFC; 1358c2ecf20Sopenharmony_ci dat |= 0x02; 1368c2ecf20Sopenharmony_ci atbm8830_write_reg(priv, REG_ADC_CONFIG, dat); 1378c2ecf20Sopenharmony_ci 1388c2ecf20Sopenharmony_ci if (priv->config->zif_swap_iq) 1398c2ecf20Sopenharmony_ci atbm8830_write_reg(priv, REG_SWAP_I_Q, 0x03); 1408c2ecf20Sopenharmony_ci else 1418c2ecf20Sopenharmony_ci atbm8830_write_reg(priv, REG_SWAP_I_Q, 0x01); 1428c2ecf20Sopenharmony_ci } 1438c2ecf20Sopenharmony_ci 1448c2ecf20Sopenharmony_ci return 0; 1458c2ecf20Sopenharmony_ci} 1468c2ecf20Sopenharmony_ci 1478c2ecf20Sopenharmony_cistatic int is_locked(struct atbm_state *priv, u8 *locked) 1488c2ecf20Sopenharmony_ci{ 1498c2ecf20Sopenharmony_ci u8 status; 1508c2ecf20Sopenharmony_ci 1518c2ecf20Sopenharmony_ci atbm8830_read_reg(priv, REG_LOCK_STATUS, &status); 1528c2ecf20Sopenharmony_ci 1538c2ecf20Sopenharmony_ci if (locked != NULL) 1548c2ecf20Sopenharmony_ci *locked = (status == 1); 1558c2ecf20Sopenharmony_ci return 0; 1568c2ecf20Sopenharmony_ci} 1578c2ecf20Sopenharmony_ci 1588c2ecf20Sopenharmony_cistatic int set_agc_config(struct atbm_state *priv, 1598c2ecf20Sopenharmony_ci u8 min, u8 max, u8 hold_loop) 1608c2ecf20Sopenharmony_ci{ 1618c2ecf20Sopenharmony_ci /* no effect if both min and max are zero */ 1628c2ecf20Sopenharmony_ci if (!min && !max) 1638c2ecf20Sopenharmony_ci return 0; 1648c2ecf20Sopenharmony_ci 1658c2ecf20Sopenharmony_ci atbm8830_write_reg(priv, REG_AGC_MIN, min); 1668c2ecf20Sopenharmony_ci atbm8830_write_reg(priv, REG_AGC_MAX, max); 1678c2ecf20Sopenharmony_ci atbm8830_write_reg(priv, REG_AGC_HOLD_LOOP, hold_loop); 1688c2ecf20Sopenharmony_ci 1698c2ecf20Sopenharmony_ci return 0; 1708c2ecf20Sopenharmony_ci} 1718c2ecf20Sopenharmony_ci 1728c2ecf20Sopenharmony_cistatic int set_static_channel_mode(struct atbm_state *priv) 1738c2ecf20Sopenharmony_ci{ 1748c2ecf20Sopenharmony_ci int i; 1758c2ecf20Sopenharmony_ci 1768c2ecf20Sopenharmony_ci for (i = 0; i < 5; i++) 1778c2ecf20Sopenharmony_ci atbm8830_write_reg(priv, 0x099B + i, 0x08); 1788c2ecf20Sopenharmony_ci 1798c2ecf20Sopenharmony_ci atbm8830_write_reg(priv, 0x095B, 0x7F); 1808c2ecf20Sopenharmony_ci atbm8830_write_reg(priv, 0x09CB, 0x01); 1818c2ecf20Sopenharmony_ci atbm8830_write_reg(priv, 0x09CC, 0x7F); 1828c2ecf20Sopenharmony_ci atbm8830_write_reg(priv, 0x09CD, 0x7F); 1838c2ecf20Sopenharmony_ci atbm8830_write_reg(priv, 0x0E01, 0x20); 1848c2ecf20Sopenharmony_ci 1858c2ecf20Sopenharmony_ci /* For single carrier */ 1868c2ecf20Sopenharmony_ci atbm8830_write_reg(priv, 0x0B03, 0x0A); 1878c2ecf20Sopenharmony_ci atbm8830_write_reg(priv, 0x0935, 0x10); 1888c2ecf20Sopenharmony_ci atbm8830_write_reg(priv, 0x0936, 0x08); 1898c2ecf20Sopenharmony_ci atbm8830_write_reg(priv, 0x093E, 0x08); 1908c2ecf20Sopenharmony_ci atbm8830_write_reg(priv, 0x096E, 0x06); 1918c2ecf20Sopenharmony_ci 1928c2ecf20Sopenharmony_ci /* frame_count_max0 */ 1938c2ecf20Sopenharmony_ci atbm8830_write_reg(priv, 0x0B09, 0x00); 1948c2ecf20Sopenharmony_ci /* frame_count_max1 */ 1958c2ecf20Sopenharmony_ci atbm8830_write_reg(priv, 0x0B0A, 0x08); 1968c2ecf20Sopenharmony_ci 1978c2ecf20Sopenharmony_ci return 0; 1988c2ecf20Sopenharmony_ci} 1998c2ecf20Sopenharmony_ci 2008c2ecf20Sopenharmony_cistatic int set_ts_config(struct atbm_state *priv) 2018c2ecf20Sopenharmony_ci{ 2028c2ecf20Sopenharmony_ci const struct atbm8830_config *cfg = priv->config; 2038c2ecf20Sopenharmony_ci 2048c2ecf20Sopenharmony_ci /*Set parallel/serial ts mode*/ 2058c2ecf20Sopenharmony_ci atbm8830_write_reg(priv, REG_TS_SERIAL, cfg->serial_ts ? 1 : 0); 2068c2ecf20Sopenharmony_ci atbm8830_write_reg(priv, REG_TS_CLK_MODE, cfg->serial_ts ? 1 : 0); 2078c2ecf20Sopenharmony_ci /*Set ts sampling edge*/ 2088c2ecf20Sopenharmony_ci atbm8830_write_reg(priv, REG_TS_SAMPLE_EDGE, 2098c2ecf20Sopenharmony_ci cfg->ts_sampling_edge ? 1 : 0); 2108c2ecf20Sopenharmony_ci /*Set ts clock freerun*/ 2118c2ecf20Sopenharmony_ci atbm8830_write_reg(priv, REG_TS_CLK_FREERUN, 2128c2ecf20Sopenharmony_ci cfg->ts_clk_gated ? 0 : 1); 2138c2ecf20Sopenharmony_ci 2148c2ecf20Sopenharmony_ci return 0; 2158c2ecf20Sopenharmony_ci} 2168c2ecf20Sopenharmony_ci 2178c2ecf20Sopenharmony_cistatic int atbm8830_init(struct dvb_frontend *fe) 2188c2ecf20Sopenharmony_ci{ 2198c2ecf20Sopenharmony_ci struct atbm_state *priv = fe->demodulator_priv; 2208c2ecf20Sopenharmony_ci const struct atbm8830_config *cfg = priv->config; 2218c2ecf20Sopenharmony_ci 2228c2ecf20Sopenharmony_ci /*Set oscillator frequency*/ 2238c2ecf20Sopenharmony_ci set_osc_freq(priv, cfg->osc_clk_freq); 2248c2ecf20Sopenharmony_ci 2258c2ecf20Sopenharmony_ci /*Set IF frequency*/ 2268c2ecf20Sopenharmony_ci set_if_freq(priv, cfg->if_freq); 2278c2ecf20Sopenharmony_ci 2288c2ecf20Sopenharmony_ci /*Set AGC Config*/ 2298c2ecf20Sopenharmony_ci set_agc_config(priv, cfg->agc_min, cfg->agc_max, 2308c2ecf20Sopenharmony_ci cfg->agc_hold_loop); 2318c2ecf20Sopenharmony_ci 2328c2ecf20Sopenharmony_ci /*Set static channel mode*/ 2338c2ecf20Sopenharmony_ci set_static_channel_mode(priv); 2348c2ecf20Sopenharmony_ci 2358c2ecf20Sopenharmony_ci set_ts_config(priv); 2368c2ecf20Sopenharmony_ci /*Turn off DSP reset*/ 2378c2ecf20Sopenharmony_ci atbm8830_write_reg(priv, 0x000A, 0); 2388c2ecf20Sopenharmony_ci 2398c2ecf20Sopenharmony_ci /*SW version test*/ 2408c2ecf20Sopenharmony_ci atbm8830_write_reg(priv, 0x020C, 11); 2418c2ecf20Sopenharmony_ci 2428c2ecf20Sopenharmony_ci /* Run */ 2438c2ecf20Sopenharmony_ci atbm8830_write_reg(priv, REG_DEMOD_RUN, 1); 2448c2ecf20Sopenharmony_ci 2458c2ecf20Sopenharmony_ci return 0; 2468c2ecf20Sopenharmony_ci} 2478c2ecf20Sopenharmony_ci 2488c2ecf20Sopenharmony_ci 2498c2ecf20Sopenharmony_cistatic void atbm8830_release(struct dvb_frontend *fe) 2508c2ecf20Sopenharmony_ci{ 2518c2ecf20Sopenharmony_ci struct atbm_state *state = fe->demodulator_priv; 2528c2ecf20Sopenharmony_ci dprintk("%s\n", __func__); 2538c2ecf20Sopenharmony_ci 2548c2ecf20Sopenharmony_ci kfree(state); 2558c2ecf20Sopenharmony_ci} 2568c2ecf20Sopenharmony_ci 2578c2ecf20Sopenharmony_cistatic int atbm8830_set_fe(struct dvb_frontend *fe) 2588c2ecf20Sopenharmony_ci{ 2598c2ecf20Sopenharmony_ci struct atbm_state *priv = fe->demodulator_priv; 2608c2ecf20Sopenharmony_ci int i; 2618c2ecf20Sopenharmony_ci u8 locked = 0; 2628c2ecf20Sopenharmony_ci dprintk("%s\n", __func__); 2638c2ecf20Sopenharmony_ci 2648c2ecf20Sopenharmony_ci /* set frequency */ 2658c2ecf20Sopenharmony_ci if (fe->ops.tuner_ops.set_params) { 2668c2ecf20Sopenharmony_ci if (fe->ops.i2c_gate_ctrl) 2678c2ecf20Sopenharmony_ci fe->ops.i2c_gate_ctrl(fe, 1); 2688c2ecf20Sopenharmony_ci fe->ops.tuner_ops.set_params(fe); 2698c2ecf20Sopenharmony_ci if (fe->ops.i2c_gate_ctrl) 2708c2ecf20Sopenharmony_ci fe->ops.i2c_gate_ctrl(fe, 0); 2718c2ecf20Sopenharmony_ci } 2728c2ecf20Sopenharmony_ci 2738c2ecf20Sopenharmony_ci /* start auto lock */ 2748c2ecf20Sopenharmony_ci for (i = 0; i < 10; i++) { 2758c2ecf20Sopenharmony_ci mdelay(100); 2768c2ecf20Sopenharmony_ci dprintk("Try %d\n", i); 2778c2ecf20Sopenharmony_ci is_locked(priv, &locked); 2788c2ecf20Sopenharmony_ci if (locked != 0) { 2798c2ecf20Sopenharmony_ci dprintk("ATBM8830 locked!\n"); 2808c2ecf20Sopenharmony_ci break; 2818c2ecf20Sopenharmony_ci } 2828c2ecf20Sopenharmony_ci } 2838c2ecf20Sopenharmony_ci 2848c2ecf20Sopenharmony_ci return 0; 2858c2ecf20Sopenharmony_ci} 2868c2ecf20Sopenharmony_ci 2878c2ecf20Sopenharmony_cistatic int atbm8830_get_fe(struct dvb_frontend *fe, 2888c2ecf20Sopenharmony_ci struct dtv_frontend_properties *c) 2898c2ecf20Sopenharmony_ci{ 2908c2ecf20Sopenharmony_ci dprintk("%s\n", __func__); 2918c2ecf20Sopenharmony_ci 2928c2ecf20Sopenharmony_ci /* TODO: get real readings from device */ 2938c2ecf20Sopenharmony_ci /* inversion status */ 2948c2ecf20Sopenharmony_ci c->inversion = INVERSION_OFF; 2958c2ecf20Sopenharmony_ci 2968c2ecf20Sopenharmony_ci /* bandwidth */ 2978c2ecf20Sopenharmony_ci c->bandwidth_hz = 8000000; 2988c2ecf20Sopenharmony_ci 2998c2ecf20Sopenharmony_ci c->code_rate_HP = FEC_AUTO; 3008c2ecf20Sopenharmony_ci c->code_rate_LP = FEC_AUTO; 3018c2ecf20Sopenharmony_ci 3028c2ecf20Sopenharmony_ci c->modulation = QAM_AUTO; 3038c2ecf20Sopenharmony_ci 3048c2ecf20Sopenharmony_ci /* transmission mode */ 3058c2ecf20Sopenharmony_ci c->transmission_mode = TRANSMISSION_MODE_AUTO; 3068c2ecf20Sopenharmony_ci 3078c2ecf20Sopenharmony_ci /* guard interval */ 3088c2ecf20Sopenharmony_ci c->guard_interval = GUARD_INTERVAL_AUTO; 3098c2ecf20Sopenharmony_ci 3108c2ecf20Sopenharmony_ci /* hierarchy */ 3118c2ecf20Sopenharmony_ci c->hierarchy = HIERARCHY_NONE; 3128c2ecf20Sopenharmony_ci 3138c2ecf20Sopenharmony_ci return 0; 3148c2ecf20Sopenharmony_ci} 3158c2ecf20Sopenharmony_ci 3168c2ecf20Sopenharmony_cistatic int atbm8830_get_tune_settings(struct dvb_frontend *fe, 3178c2ecf20Sopenharmony_ci struct dvb_frontend_tune_settings *fesettings) 3188c2ecf20Sopenharmony_ci{ 3198c2ecf20Sopenharmony_ci fesettings->min_delay_ms = 0; 3208c2ecf20Sopenharmony_ci fesettings->step_size = 0; 3218c2ecf20Sopenharmony_ci fesettings->max_drift = 0; 3228c2ecf20Sopenharmony_ci return 0; 3238c2ecf20Sopenharmony_ci} 3248c2ecf20Sopenharmony_ci 3258c2ecf20Sopenharmony_cistatic int atbm8830_read_status(struct dvb_frontend *fe, 3268c2ecf20Sopenharmony_ci enum fe_status *fe_status) 3278c2ecf20Sopenharmony_ci{ 3288c2ecf20Sopenharmony_ci struct atbm_state *priv = fe->demodulator_priv; 3298c2ecf20Sopenharmony_ci u8 locked = 0; 3308c2ecf20Sopenharmony_ci u8 agc_locked = 0; 3318c2ecf20Sopenharmony_ci 3328c2ecf20Sopenharmony_ci dprintk("%s\n", __func__); 3338c2ecf20Sopenharmony_ci *fe_status = 0; 3348c2ecf20Sopenharmony_ci 3358c2ecf20Sopenharmony_ci is_locked(priv, &locked); 3368c2ecf20Sopenharmony_ci if (locked) { 3378c2ecf20Sopenharmony_ci *fe_status |= FE_HAS_SIGNAL | FE_HAS_CARRIER | 3388c2ecf20Sopenharmony_ci FE_HAS_VITERBI | FE_HAS_SYNC | FE_HAS_LOCK; 3398c2ecf20Sopenharmony_ci } 3408c2ecf20Sopenharmony_ci dprintk("%s: fe_status=0x%x\n", __func__, *fe_status); 3418c2ecf20Sopenharmony_ci 3428c2ecf20Sopenharmony_ci atbm8830_read_reg(priv, REG_AGC_LOCK, &agc_locked); 3438c2ecf20Sopenharmony_ci dprintk("AGC Lock: %d\n", agc_locked); 3448c2ecf20Sopenharmony_ci 3458c2ecf20Sopenharmony_ci return 0; 3468c2ecf20Sopenharmony_ci} 3478c2ecf20Sopenharmony_ci 3488c2ecf20Sopenharmony_cistatic int atbm8830_read_ber(struct dvb_frontend *fe, u32 *ber) 3498c2ecf20Sopenharmony_ci{ 3508c2ecf20Sopenharmony_ci struct atbm_state *priv = fe->demodulator_priv; 3518c2ecf20Sopenharmony_ci u32 frame_err; 3528c2ecf20Sopenharmony_ci u8 t; 3538c2ecf20Sopenharmony_ci 3548c2ecf20Sopenharmony_ci dprintk("%s\n", __func__); 3558c2ecf20Sopenharmony_ci 3568c2ecf20Sopenharmony_ci atbm8830_reglatch_lock(priv, 1); 3578c2ecf20Sopenharmony_ci 3588c2ecf20Sopenharmony_ci atbm8830_read_reg(priv, REG_FRAME_ERR_CNT + 1, &t); 3598c2ecf20Sopenharmony_ci frame_err = t & 0x7F; 3608c2ecf20Sopenharmony_ci frame_err <<= 8; 3618c2ecf20Sopenharmony_ci atbm8830_read_reg(priv, REG_FRAME_ERR_CNT, &t); 3628c2ecf20Sopenharmony_ci frame_err |= t; 3638c2ecf20Sopenharmony_ci 3648c2ecf20Sopenharmony_ci atbm8830_reglatch_lock(priv, 0); 3658c2ecf20Sopenharmony_ci 3668c2ecf20Sopenharmony_ci *ber = frame_err * 100 / 32767; 3678c2ecf20Sopenharmony_ci 3688c2ecf20Sopenharmony_ci dprintk("%s: ber=0x%x\n", __func__, *ber); 3698c2ecf20Sopenharmony_ci return 0; 3708c2ecf20Sopenharmony_ci} 3718c2ecf20Sopenharmony_ci 3728c2ecf20Sopenharmony_cistatic int atbm8830_read_signal_strength(struct dvb_frontend *fe, u16 *signal) 3738c2ecf20Sopenharmony_ci{ 3748c2ecf20Sopenharmony_ci struct atbm_state *priv = fe->demodulator_priv; 3758c2ecf20Sopenharmony_ci u32 pwm; 3768c2ecf20Sopenharmony_ci u8 t; 3778c2ecf20Sopenharmony_ci 3788c2ecf20Sopenharmony_ci dprintk("%s\n", __func__); 3798c2ecf20Sopenharmony_ci atbm8830_reglatch_lock(priv, 1); 3808c2ecf20Sopenharmony_ci 3818c2ecf20Sopenharmony_ci atbm8830_read_reg(priv, REG_AGC_PWM_VAL + 1, &t); 3828c2ecf20Sopenharmony_ci pwm = t & 0x03; 3838c2ecf20Sopenharmony_ci pwm <<= 8; 3848c2ecf20Sopenharmony_ci atbm8830_read_reg(priv, REG_AGC_PWM_VAL, &t); 3858c2ecf20Sopenharmony_ci pwm |= t; 3868c2ecf20Sopenharmony_ci 3878c2ecf20Sopenharmony_ci atbm8830_reglatch_lock(priv, 0); 3888c2ecf20Sopenharmony_ci 3898c2ecf20Sopenharmony_ci dprintk("AGC PWM = 0x%02X\n", pwm); 3908c2ecf20Sopenharmony_ci pwm = 0x400 - pwm; 3918c2ecf20Sopenharmony_ci 3928c2ecf20Sopenharmony_ci *signal = pwm * 0x10000 / 0x400; 3938c2ecf20Sopenharmony_ci 3948c2ecf20Sopenharmony_ci return 0; 3958c2ecf20Sopenharmony_ci} 3968c2ecf20Sopenharmony_ci 3978c2ecf20Sopenharmony_cistatic int atbm8830_read_snr(struct dvb_frontend *fe, u16 *snr) 3988c2ecf20Sopenharmony_ci{ 3998c2ecf20Sopenharmony_ci dprintk("%s\n", __func__); 4008c2ecf20Sopenharmony_ci *snr = 0; 4018c2ecf20Sopenharmony_ci return 0; 4028c2ecf20Sopenharmony_ci} 4038c2ecf20Sopenharmony_ci 4048c2ecf20Sopenharmony_cistatic int atbm8830_read_ucblocks(struct dvb_frontend *fe, u32 *ucblocks) 4058c2ecf20Sopenharmony_ci{ 4068c2ecf20Sopenharmony_ci dprintk("%s\n", __func__); 4078c2ecf20Sopenharmony_ci *ucblocks = 0; 4088c2ecf20Sopenharmony_ci return 0; 4098c2ecf20Sopenharmony_ci} 4108c2ecf20Sopenharmony_ci 4118c2ecf20Sopenharmony_cistatic int atbm8830_i2c_gate_ctrl(struct dvb_frontend *fe, int enable) 4128c2ecf20Sopenharmony_ci{ 4138c2ecf20Sopenharmony_ci struct atbm_state *priv = fe->demodulator_priv; 4148c2ecf20Sopenharmony_ci 4158c2ecf20Sopenharmony_ci return atbm8830_write_reg(priv, REG_I2C_GATE, enable ? 1 : 0); 4168c2ecf20Sopenharmony_ci} 4178c2ecf20Sopenharmony_ci 4188c2ecf20Sopenharmony_cistatic const struct dvb_frontend_ops atbm8830_ops = { 4198c2ecf20Sopenharmony_ci .delsys = { SYS_DTMB }, 4208c2ecf20Sopenharmony_ci .info = { 4218c2ecf20Sopenharmony_ci .name = "AltoBeam ATBM8830/8831 DMB-TH", 4228c2ecf20Sopenharmony_ci .frequency_min_hz = 474 * MHz, 4238c2ecf20Sopenharmony_ci .frequency_max_hz = 858 * MHz, 4248c2ecf20Sopenharmony_ci .frequency_stepsize_hz = 10 * kHz, 4258c2ecf20Sopenharmony_ci .caps = 4268c2ecf20Sopenharmony_ci FE_CAN_FEC_AUTO | 4278c2ecf20Sopenharmony_ci FE_CAN_QAM_AUTO | 4288c2ecf20Sopenharmony_ci FE_CAN_TRANSMISSION_MODE_AUTO | 4298c2ecf20Sopenharmony_ci FE_CAN_GUARD_INTERVAL_AUTO 4308c2ecf20Sopenharmony_ci }, 4318c2ecf20Sopenharmony_ci 4328c2ecf20Sopenharmony_ci .release = atbm8830_release, 4338c2ecf20Sopenharmony_ci 4348c2ecf20Sopenharmony_ci .init = atbm8830_init, 4358c2ecf20Sopenharmony_ci .sleep = NULL, 4368c2ecf20Sopenharmony_ci .write = NULL, 4378c2ecf20Sopenharmony_ci .i2c_gate_ctrl = atbm8830_i2c_gate_ctrl, 4388c2ecf20Sopenharmony_ci 4398c2ecf20Sopenharmony_ci .set_frontend = atbm8830_set_fe, 4408c2ecf20Sopenharmony_ci .get_frontend = atbm8830_get_fe, 4418c2ecf20Sopenharmony_ci .get_tune_settings = atbm8830_get_tune_settings, 4428c2ecf20Sopenharmony_ci 4438c2ecf20Sopenharmony_ci .read_status = atbm8830_read_status, 4448c2ecf20Sopenharmony_ci .read_ber = atbm8830_read_ber, 4458c2ecf20Sopenharmony_ci .read_signal_strength = atbm8830_read_signal_strength, 4468c2ecf20Sopenharmony_ci .read_snr = atbm8830_read_snr, 4478c2ecf20Sopenharmony_ci .read_ucblocks = atbm8830_read_ucblocks, 4488c2ecf20Sopenharmony_ci}; 4498c2ecf20Sopenharmony_ci 4508c2ecf20Sopenharmony_cistruct dvb_frontend *atbm8830_attach(const struct atbm8830_config *config, 4518c2ecf20Sopenharmony_ci struct i2c_adapter *i2c) 4528c2ecf20Sopenharmony_ci{ 4538c2ecf20Sopenharmony_ci struct atbm_state *priv = NULL; 4548c2ecf20Sopenharmony_ci u8 data = 0; 4558c2ecf20Sopenharmony_ci 4568c2ecf20Sopenharmony_ci dprintk("%s()\n", __func__); 4578c2ecf20Sopenharmony_ci 4588c2ecf20Sopenharmony_ci if (config == NULL || i2c == NULL) 4598c2ecf20Sopenharmony_ci return NULL; 4608c2ecf20Sopenharmony_ci 4618c2ecf20Sopenharmony_ci priv = kzalloc(sizeof(struct atbm_state), GFP_KERNEL); 4628c2ecf20Sopenharmony_ci if (priv == NULL) 4638c2ecf20Sopenharmony_ci goto error_out; 4648c2ecf20Sopenharmony_ci 4658c2ecf20Sopenharmony_ci priv->config = config; 4668c2ecf20Sopenharmony_ci priv->i2c = i2c; 4678c2ecf20Sopenharmony_ci 4688c2ecf20Sopenharmony_ci /* check if the demod is there */ 4698c2ecf20Sopenharmony_ci if (atbm8830_read_reg(priv, REG_CHIP_ID, &data) != 0) { 4708c2ecf20Sopenharmony_ci dprintk("%s atbm8830/8831 not found at i2c addr 0x%02X\n", 4718c2ecf20Sopenharmony_ci __func__, priv->config->demod_address); 4728c2ecf20Sopenharmony_ci goto error_out; 4738c2ecf20Sopenharmony_ci } 4748c2ecf20Sopenharmony_ci dprintk("atbm8830 chip id: 0x%02X\n", data); 4758c2ecf20Sopenharmony_ci 4768c2ecf20Sopenharmony_ci memcpy(&priv->frontend.ops, &atbm8830_ops, 4778c2ecf20Sopenharmony_ci sizeof(struct dvb_frontend_ops)); 4788c2ecf20Sopenharmony_ci priv->frontend.demodulator_priv = priv; 4798c2ecf20Sopenharmony_ci 4808c2ecf20Sopenharmony_ci atbm8830_init(&priv->frontend); 4818c2ecf20Sopenharmony_ci 4828c2ecf20Sopenharmony_ci atbm8830_i2c_gate_ctrl(&priv->frontend, 1); 4838c2ecf20Sopenharmony_ci 4848c2ecf20Sopenharmony_ci return &priv->frontend; 4858c2ecf20Sopenharmony_ci 4868c2ecf20Sopenharmony_cierror_out: 4878c2ecf20Sopenharmony_ci dprintk("%s() error_out\n", __func__); 4888c2ecf20Sopenharmony_ci kfree(priv); 4898c2ecf20Sopenharmony_ci return NULL; 4908c2ecf20Sopenharmony_ci 4918c2ecf20Sopenharmony_ci} 4928c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(atbm8830_attach); 4938c2ecf20Sopenharmony_ci 4948c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("AltoBeam ATBM8830/8831 GB20600 demodulator driver"); 4958c2ecf20Sopenharmony_ciMODULE_AUTHOR("David T. L. Wong <davidtlwong@gmail.com>"); 4968c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 497