18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Driver for Conexant CX24113/CX24128 Tuner (Satellite) 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (C) 2007-8 Patrick Boettcher <pb@linuxtv.org> 68c2ecf20Sopenharmony_ci * 78c2ecf20Sopenharmony_ci * Developed for BBTI / Technisat 88c2ecf20Sopenharmony_ci */ 98c2ecf20Sopenharmony_ci 108c2ecf20Sopenharmony_ci#include <linux/slab.h> 118c2ecf20Sopenharmony_ci#include <linux/kernel.h> 128c2ecf20Sopenharmony_ci#include <linux/module.h> 138c2ecf20Sopenharmony_ci#include <linux/init.h> 148c2ecf20Sopenharmony_ci 158c2ecf20Sopenharmony_ci#include <media/dvb_frontend.h> 168c2ecf20Sopenharmony_ci#include "cx24113.h" 178c2ecf20Sopenharmony_ci 188c2ecf20Sopenharmony_cistatic int debug; 198c2ecf20Sopenharmony_ci 208c2ecf20Sopenharmony_ci#define cx_info(args...) do { printk(KERN_INFO "CX24113: " args); } while (0) 218c2ecf20Sopenharmony_ci#define cx_err(args...) do { printk(KERN_ERR "CX24113: " args); } while (0) 228c2ecf20Sopenharmony_ci 238c2ecf20Sopenharmony_ci#define dprintk(args...) \ 248c2ecf20Sopenharmony_ci do { \ 258c2ecf20Sopenharmony_ci if (debug) { \ 268c2ecf20Sopenharmony_ci printk(KERN_DEBUG "CX24113: %s: ", __func__); \ 278c2ecf20Sopenharmony_ci printk(args); \ 288c2ecf20Sopenharmony_ci } \ 298c2ecf20Sopenharmony_ci } while (0) 308c2ecf20Sopenharmony_ci 318c2ecf20Sopenharmony_cistruct cx24113_state { 328c2ecf20Sopenharmony_ci struct i2c_adapter *i2c; 338c2ecf20Sopenharmony_ci const struct cx24113_config *config; 348c2ecf20Sopenharmony_ci 358c2ecf20Sopenharmony_ci#define REV_CX24113 0x23 368c2ecf20Sopenharmony_ci u8 rev; 378c2ecf20Sopenharmony_ci u8 ver; 388c2ecf20Sopenharmony_ci 398c2ecf20Sopenharmony_ci u8 icp_mode:1; 408c2ecf20Sopenharmony_ci 418c2ecf20Sopenharmony_ci#define ICP_LEVEL1 0 428c2ecf20Sopenharmony_ci#define ICP_LEVEL2 1 438c2ecf20Sopenharmony_ci#define ICP_LEVEL3 2 448c2ecf20Sopenharmony_ci#define ICP_LEVEL4 3 458c2ecf20Sopenharmony_ci u8 icp_man:2; 468c2ecf20Sopenharmony_ci u8 icp_auto_low:2; 478c2ecf20Sopenharmony_ci u8 icp_auto_mlow:2; 488c2ecf20Sopenharmony_ci u8 icp_auto_mhi:2; 498c2ecf20Sopenharmony_ci u8 icp_auto_hi:2; 508c2ecf20Sopenharmony_ci u8 icp_dig; 518c2ecf20Sopenharmony_ci 528c2ecf20Sopenharmony_ci#define LNA_MIN_GAIN 0 538c2ecf20Sopenharmony_ci#define LNA_MID_GAIN 1 548c2ecf20Sopenharmony_ci#define LNA_MAX_GAIN 2 558c2ecf20Sopenharmony_ci u8 lna_gain:2; 568c2ecf20Sopenharmony_ci 578c2ecf20Sopenharmony_ci u8 acp_on:1; 588c2ecf20Sopenharmony_ci 598c2ecf20Sopenharmony_ci u8 vco_mode:2; 608c2ecf20Sopenharmony_ci u8 vco_shift:1; 618c2ecf20Sopenharmony_ci#define VCOBANDSEL_6 0x80 628c2ecf20Sopenharmony_ci#define VCOBANDSEL_5 0x01 638c2ecf20Sopenharmony_ci#define VCOBANDSEL_4 0x02 648c2ecf20Sopenharmony_ci#define VCOBANDSEL_3 0x04 658c2ecf20Sopenharmony_ci#define VCOBANDSEL_2 0x08 668c2ecf20Sopenharmony_ci#define VCOBANDSEL_1 0x10 678c2ecf20Sopenharmony_ci u8 vco_band; 688c2ecf20Sopenharmony_ci 698c2ecf20Sopenharmony_ci#define VCODIV4 4 708c2ecf20Sopenharmony_ci#define VCODIV2 2 718c2ecf20Sopenharmony_ci u8 vcodiv; 728c2ecf20Sopenharmony_ci 738c2ecf20Sopenharmony_ci u8 bs_delay:4; 748c2ecf20Sopenharmony_ci u16 bs_freqcnt:13; 758c2ecf20Sopenharmony_ci u16 bs_rdiv; 768c2ecf20Sopenharmony_ci u8 prescaler_mode:1; 778c2ecf20Sopenharmony_ci 788c2ecf20Sopenharmony_ci u8 rfvga_bias_ctrl; 798c2ecf20Sopenharmony_ci 808c2ecf20Sopenharmony_ci s16 tuner_gain_thres; 818c2ecf20Sopenharmony_ci u8 gain_level; 828c2ecf20Sopenharmony_ci 838c2ecf20Sopenharmony_ci u32 frequency; 848c2ecf20Sopenharmony_ci 858c2ecf20Sopenharmony_ci u8 refdiv; 868c2ecf20Sopenharmony_ci 878c2ecf20Sopenharmony_ci u8 Fwindow_enabled; 888c2ecf20Sopenharmony_ci}; 898c2ecf20Sopenharmony_ci 908c2ecf20Sopenharmony_cistatic int cx24113_writereg(struct cx24113_state *state, int reg, int data) 918c2ecf20Sopenharmony_ci{ 928c2ecf20Sopenharmony_ci u8 buf[] = { reg, data }; 938c2ecf20Sopenharmony_ci struct i2c_msg msg = { .addr = state->config->i2c_addr, 948c2ecf20Sopenharmony_ci .flags = 0, .buf = buf, .len = 2 }; 958c2ecf20Sopenharmony_ci int err = i2c_transfer(state->i2c, &msg, 1); 968c2ecf20Sopenharmony_ci if (err != 1) { 978c2ecf20Sopenharmony_ci printk(KERN_DEBUG "%s: writereg error(err == %i, reg == 0x%02x, data == 0x%02x)\n", 988c2ecf20Sopenharmony_ci __func__, err, reg, data); 998c2ecf20Sopenharmony_ci return err; 1008c2ecf20Sopenharmony_ci } 1018c2ecf20Sopenharmony_ci 1028c2ecf20Sopenharmony_ci return 0; 1038c2ecf20Sopenharmony_ci} 1048c2ecf20Sopenharmony_ci 1058c2ecf20Sopenharmony_cistatic int cx24113_readreg(struct cx24113_state *state, u8 reg) 1068c2ecf20Sopenharmony_ci{ 1078c2ecf20Sopenharmony_ci int ret; 1088c2ecf20Sopenharmony_ci u8 b; 1098c2ecf20Sopenharmony_ci struct i2c_msg msg[] = { 1108c2ecf20Sopenharmony_ci { .addr = state->config->i2c_addr, 1118c2ecf20Sopenharmony_ci .flags = 0, .buf = ®, .len = 1 }, 1128c2ecf20Sopenharmony_ci { .addr = state->config->i2c_addr, 1138c2ecf20Sopenharmony_ci .flags = I2C_M_RD, .buf = &b, .len = 1 } 1148c2ecf20Sopenharmony_ci }; 1158c2ecf20Sopenharmony_ci 1168c2ecf20Sopenharmony_ci ret = i2c_transfer(state->i2c, msg, 2); 1178c2ecf20Sopenharmony_ci 1188c2ecf20Sopenharmony_ci if (ret != 2) { 1198c2ecf20Sopenharmony_ci printk(KERN_DEBUG "%s: reg=0x%x (error=%d)\n", 1208c2ecf20Sopenharmony_ci __func__, reg, ret); 1218c2ecf20Sopenharmony_ci return ret; 1228c2ecf20Sopenharmony_ci } 1238c2ecf20Sopenharmony_ci 1248c2ecf20Sopenharmony_ci return b; 1258c2ecf20Sopenharmony_ci} 1268c2ecf20Sopenharmony_ci 1278c2ecf20Sopenharmony_cistatic void cx24113_set_parameters(struct cx24113_state *state) 1288c2ecf20Sopenharmony_ci{ 1298c2ecf20Sopenharmony_ci u8 r; 1308c2ecf20Sopenharmony_ci 1318c2ecf20Sopenharmony_ci r = cx24113_readreg(state, 0x10) & 0x82; 1328c2ecf20Sopenharmony_ci r |= state->icp_mode; 1338c2ecf20Sopenharmony_ci r |= state->icp_man << 4; 1348c2ecf20Sopenharmony_ci r |= state->icp_dig << 2; 1358c2ecf20Sopenharmony_ci r |= state->prescaler_mode << 5; 1368c2ecf20Sopenharmony_ci cx24113_writereg(state, 0x10, r); 1378c2ecf20Sopenharmony_ci 1388c2ecf20Sopenharmony_ci r = (state->icp_auto_low << 0) | (state->icp_auto_mlow << 2) 1398c2ecf20Sopenharmony_ci | (state->icp_auto_mhi << 4) | (state->icp_auto_hi << 6); 1408c2ecf20Sopenharmony_ci cx24113_writereg(state, 0x11, r); 1418c2ecf20Sopenharmony_ci 1428c2ecf20Sopenharmony_ci if (state->rev == REV_CX24113) { 1438c2ecf20Sopenharmony_ci r = cx24113_readreg(state, 0x20) & 0xec; 1448c2ecf20Sopenharmony_ci r |= state->lna_gain; 1458c2ecf20Sopenharmony_ci r |= state->rfvga_bias_ctrl << 4; 1468c2ecf20Sopenharmony_ci cx24113_writereg(state, 0x20, r); 1478c2ecf20Sopenharmony_ci } 1488c2ecf20Sopenharmony_ci 1498c2ecf20Sopenharmony_ci r = cx24113_readreg(state, 0x12) & 0x03; 1508c2ecf20Sopenharmony_ci r |= state->acp_on << 2; 1518c2ecf20Sopenharmony_ci r |= state->bs_delay << 4; 1528c2ecf20Sopenharmony_ci cx24113_writereg(state, 0x12, r); 1538c2ecf20Sopenharmony_ci 1548c2ecf20Sopenharmony_ci r = cx24113_readreg(state, 0x18) & 0x40; 1558c2ecf20Sopenharmony_ci r |= state->vco_shift; 1568c2ecf20Sopenharmony_ci if (state->vco_band == VCOBANDSEL_6) 1578c2ecf20Sopenharmony_ci r |= (1 << 7); 1588c2ecf20Sopenharmony_ci else 1598c2ecf20Sopenharmony_ci r |= (state->vco_band << 1); 1608c2ecf20Sopenharmony_ci cx24113_writereg(state, 0x18, r); 1618c2ecf20Sopenharmony_ci 1628c2ecf20Sopenharmony_ci r = cx24113_readreg(state, 0x14) & 0x20; 1638c2ecf20Sopenharmony_ci r |= (state->vco_mode << 6) | ((state->bs_freqcnt >> 8) & 0x1f); 1648c2ecf20Sopenharmony_ci cx24113_writereg(state, 0x14, r); 1658c2ecf20Sopenharmony_ci cx24113_writereg(state, 0x15, (state->bs_freqcnt & 0xff)); 1668c2ecf20Sopenharmony_ci 1678c2ecf20Sopenharmony_ci cx24113_writereg(state, 0x16, (state->bs_rdiv >> 4) & 0xff); 1688c2ecf20Sopenharmony_ci r = (cx24113_readreg(state, 0x17) & 0x0f) | 1698c2ecf20Sopenharmony_ci ((state->bs_rdiv & 0x0f) << 4); 1708c2ecf20Sopenharmony_ci cx24113_writereg(state, 0x17, r); 1718c2ecf20Sopenharmony_ci} 1728c2ecf20Sopenharmony_ci 1738c2ecf20Sopenharmony_ci#define VGA_0 0x00 1748c2ecf20Sopenharmony_ci#define VGA_1 0x04 1758c2ecf20Sopenharmony_ci#define VGA_2 0x02 1768c2ecf20Sopenharmony_ci#define VGA_3 0x06 1778c2ecf20Sopenharmony_ci#define VGA_4 0x01 1788c2ecf20Sopenharmony_ci#define VGA_5 0x05 1798c2ecf20Sopenharmony_ci#define VGA_6 0x03 1808c2ecf20Sopenharmony_ci#define VGA_7 0x07 1818c2ecf20Sopenharmony_ci 1828c2ecf20Sopenharmony_ci#define RFVGA_0 0x00 1838c2ecf20Sopenharmony_ci#define RFVGA_1 0x01 1848c2ecf20Sopenharmony_ci#define RFVGA_2 0x02 1858c2ecf20Sopenharmony_ci#define RFVGA_3 0x03 1868c2ecf20Sopenharmony_ci 1878c2ecf20Sopenharmony_cistatic int cx24113_set_gain_settings(struct cx24113_state *state, 1888c2ecf20Sopenharmony_ci s16 power_estimation) 1898c2ecf20Sopenharmony_ci{ 1908c2ecf20Sopenharmony_ci u8 ampout = cx24113_readreg(state, 0x1d) & 0xf0, 1918c2ecf20Sopenharmony_ci vga = cx24113_readreg(state, 0x1f) & 0x3f, 1928c2ecf20Sopenharmony_ci rfvga = cx24113_readreg(state, 0x20) & 0xf3; 1938c2ecf20Sopenharmony_ci u8 gain_level = power_estimation >= state->tuner_gain_thres; 1948c2ecf20Sopenharmony_ci 1958c2ecf20Sopenharmony_ci dprintk("power estimation: %d, thres: %d, gain_level: %d/%d\n", 1968c2ecf20Sopenharmony_ci power_estimation, state->tuner_gain_thres, 1978c2ecf20Sopenharmony_ci state->gain_level, gain_level); 1988c2ecf20Sopenharmony_ci 1998c2ecf20Sopenharmony_ci if (gain_level == state->gain_level) 2008c2ecf20Sopenharmony_ci return 0; /* nothing to be done */ 2018c2ecf20Sopenharmony_ci 2028c2ecf20Sopenharmony_ci ampout |= 0xf; 2038c2ecf20Sopenharmony_ci 2048c2ecf20Sopenharmony_ci if (gain_level) { 2058c2ecf20Sopenharmony_ci rfvga |= RFVGA_0 << 2; 2068c2ecf20Sopenharmony_ci vga |= (VGA_7 << 3) | VGA_7; 2078c2ecf20Sopenharmony_ci } else { 2088c2ecf20Sopenharmony_ci rfvga |= RFVGA_2 << 2; 2098c2ecf20Sopenharmony_ci vga |= (VGA_6 << 3) | VGA_2; 2108c2ecf20Sopenharmony_ci } 2118c2ecf20Sopenharmony_ci state->gain_level = gain_level; 2128c2ecf20Sopenharmony_ci 2138c2ecf20Sopenharmony_ci cx24113_writereg(state, 0x1d, ampout); 2148c2ecf20Sopenharmony_ci cx24113_writereg(state, 0x1f, vga); 2158c2ecf20Sopenharmony_ci cx24113_writereg(state, 0x20, rfvga); 2168c2ecf20Sopenharmony_ci 2178c2ecf20Sopenharmony_ci return 1; /* did something */ 2188c2ecf20Sopenharmony_ci} 2198c2ecf20Sopenharmony_ci 2208c2ecf20Sopenharmony_cistatic int cx24113_set_Fref(struct cx24113_state *state, u8 high) 2218c2ecf20Sopenharmony_ci{ 2228c2ecf20Sopenharmony_ci u8 xtal = cx24113_readreg(state, 0x02); 2238c2ecf20Sopenharmony_ci if (state->rev == 0x43 && state->vcodiv == VCODIV4) 2248c2ecf20Sopenharmony_ci high = 1; 2258c2ecf20Sopenharmony_ci 2268c2ecf20Sopenharmony_ci xtal &= ~0x2; 2278c2ecf20Sopenharmony_ci if (high) 2288c2ecf20Sopenharmony_ci xtal |= high << 1; 2298c2ecf20Sopenharmony_ci return cx24113_writereg(state, 0x02, xtal); 2308c2ecf20Sopenharmony_ci} 2318c2ecf20Sopenharmony_ci 2328c2ecf20Sopenharmony_cistatic int cx24113_enable(struct cx24113_state *state, u8 enable) 2338c2ecf20Sopenharmony_ci{ 2348c2ecf20Sopenharmony_ci u8 r21 = (cx24113_readreg(state, 0x21) & 0xc0) | enable; 2358c2ecf20Sopenharmony_ci if (state->rev == REV_CX24113) 2368c2ecf20Sopenharmony_ci r21 |= (1 << 1); 2378c2ecf20Sopenharmony_ci return cx24113_writereg(state, 0x21, r21); 2388c2ecf20Sopenharmony_ci} 2398c2ecf20Sopenharmony_ci 2408c2ecf20Sopenharmony_cistatic int cx24113_set_bandwidth(struct cx24113_state *state, u32 bandwidth_khz) 2418c2ecf20Sopenharmony_ci{ 2428c2ecf20Sopenharmony_ci u8 r; 2438c2ecf20Sopenharmony_ci 2448c2ecf20Sopenharmony_ci if (bandwidth_khz <= 19000) 2458c2ecf20Sopenharmony_ci r = 0x03 << 6; 2468c2ecf20Sopenharmony_ci else if (bandwidth_khz <= 25000) 2478c2ecf20Sopenharmony_ci r = 0x02 << 6; 2488c2ecf20Sopenharmony_ci else 2498c2ecf20Sopenharmony_ci r = 0x01 << 6; 2508c2ecf20Sopenharmony_ci 2518c2ecf20Sopenharmony_ci dprintk("bandwidth to be set: %d\n", bandwidth_khz); 2528c2ecf20Sopenharmony_ci bandwidth_khz *= 10; 2538c2ecf20Sopenharmony_ci bandwidth_khz -= 10000; 2548c2ecf20Sopenharmony_ci bandwidth_khz /= 1000; 2558c2ecf20Sopenharmony_ci bandwidth_khz += 5; 2568c2ecf20Sopenharmony_ci bandwidth_khz /= 10; 2578c2ecf20Sopenharmony_ci 2588c2ecf20Sopenharmony_ci dprintk("bandwidth: %d %d\n", r >> 6, bandwidth_khz); 2598c2ecf20Sopenharmony_ci 2608c2ecf20Sopenharmony_ci r |= bandwidth_khz & 0x3f; 2618c2ecf20Sopenharmony_ci 2628c2ecf20Sopenharmony_ci return cx24113_writereg(state, 0x1e, r); 2638c2ecf20Sopenharmony_ci} 2648c2ecf20Sopenharmony_ci 2658c2ecf20Sopenharmony_cistatic int cx24113_set_clk_inversion(struct cx24113_state *state, u8 on) 2668c2ecf20Sopenharmony_ci{ 2678c2ecf20Sopenharmony_ci u8 r = (cx24113_readreg(state, 0x10) & 0x7f) | ((on & 0x1) << 7); 2688c2ecf20Sopenharmony_ci return cx24113_writereg(state, 0x10, r); 2698c2ecf20Sopenharmony_ci} 2708c2ecf20Sopenharmony_ci 2718c2ecf20Sopenharmony_cistatic int cx24113_get_status(struct dvb_frontend *fe, u32 *status) 2728c2ecf20Sopenharmony_ci{ 2738c2ecf20Sopenharmony_ci struct cx24113_state *state = fe->tuner_priv; 2748c2ecf20Sopenharmony_ci u8 r = (cx24113_readreg(state, 0x10) & 0x02) >> 1; 2758c2ecf20Sopenharmony_ci if (r) 2768c2ecf20Sopenharmony_ci *status |= TUNER_STATUS_LOCKED; 2778c2ecf20Sopenharmony_ci dprintk("PLL locked: %d\n", r); 2788c2ecf20Sopenharmony_ci return 0; 2798c2ecf20Sopenharmony_ci} 2808c2ecf20Sopenharmony_ci 2818c2ecf20Sopenharmony_cistatic u8 cx24113_set_ref_div(struct cx24113_state *state, u8 refdiv) 2828c2ecf20Sopenharmony_ci{ 2838c2ecf20Sopenharmony_ci if (state->rev == 0x43 && state->vcodiv == VCODIV4) 2848c2ecf20Sopenharmony_ci refdiv = 2; 2858c2ecf20Sopenharmony_ci return state->refdiv = refdiv; 2868c2ecf20Sopenharmony_ci} 2878c2ecf20Sopenharmony_ci 2888c2ecf20Sopenharmony_cistatic void cx24113_calc_pll_nf(struct cx24113_state *state, u16 *n, s32 *f) 2898c2ecf20Sopenharmony_ci{ 2908c2ecf20Sopenharmony_ci s32 N; 2918c2ecf20Sopenharmony_ci s64 F; 2928c2ecf20Sopenharmony_ci u64 dividend; 2938c2ecf20Sopenharmony_ci u8 R, r; 2948c2ecf20Sopenharmony_ci u8 vcodiv; 2958c2ecf20Sopenharmony_ci u8 factor; 2968c2ecf20Sopenharmony_ci s32 freq_hz = state->frequency * 1000; 2978c2ecf20Sopenharmony_ci 2988c2ecf20Sopenharmony_ci if (state->config->xtal_khz < 20000) 2998c2ecf20Sopenharmony_ci factor = 1; 3008c2ecf20Sopenharmony_ci else 3018c2ecf20Sopenharmony_ci factor = 2; 3028c2ecf20Sopenharmony_ci 3038c2ecf20Sopenharmony_ci if (state->rev == REV_CX24113) { 3048c2ecf20Sopenharmony_ci if (state->frequency >= 1100000) 3058c2ecf20Sopenharmony_ci vcodiv = VCODIV2; 3068c2ecf20Sopenharmony_ci else 3078c2ecf20Sopenharmony_ci vcodiv = VCODIV4; 3088c2ecf20Sopenharmony_ci } else { 3098c2ecf20Sopenharmony_ci if (state->frequency >= 1165000) 3108c2ecf20Sopenharmony_ci vcodiv = VCODIV2; 3118c2ecf20Sopenharmony_ci else 3128c2ecf20Sopenharmony_ci vcodiv = VCODIV4; 3138c2ecf20Sopenharmony_ci } 3148c2ecf20Sopenharmony_ci state->vcodiv = vcodiv; 3158c2ecf20Sopenharmony_ci 3168c2ecf20Sopenharmony_ci dprintk("calculating N/F for %dHz with vcodiv %d\n", freq_hz, vcodiv); 3178c2ecf20Sopenharmony_ci R = 0; 3188c2ecf20Sopenharmony_ci do { 3198c2ecf20Sopenharmony_ci R = cx24113_set_ref_div(state, R + 1); 3208c2ecf20Sopenharmony_ci 3218c2ecf20Sopenharmony_ci /* calculate tuner PLL settings: */ 3228c2ecf20Sopenharmony_ci N = (freq_hz / 100 * vcodiv) * R; 3238c2ecf20Sopenharmony_ci N /= (state->config->xtal_khz) * factor * 2; 3248c2ecf20Sopenharmony_ci N += 5; /* For round up. */ 3258c2ecf20Sopenharmony_ci N /= 10; 3268c2ecf20Sopenharmony_ci N -= 32; 3278c2ecf20Sopenharmony_ci } while (N < 6 && R < 3); 3288c2ecf20Sopenharmony_ci 3298c2ecf20Sopenharmony_ci if (N < 6) { 3308c2ecf20Sopenharmony_ci cx_err("strange frequency: N < 6\n"); 3318c2ecf20Sopenharmony_ci return; 3328c2ecf20Sopenharmony_ci } 3338c2ecf20Sopenharmony_ci F = freq_hz; 3348c2ecf20Sopenharmony_ci F *= (u64) (R * vcodiv * 262144); 3358c2ecf20Sopenharmony_ci dprintk("1 N: %d, F: %lld, R: %d\n", N, (long long)F, R); 3368c2ecf20Sopenharmony_ci /* do_div needs an u64 as first argument */ 3378c2ecf20Sopenharmony_ci dividend = F; 3388c2ecf20Sopenharmony_ci do_div(dividend, state->config->xtal_khz * 1000 * factor * 2); 3398c2ecf20Sopenharmony_ci F = dividend; 3408c2ecf20Sopenharmony_ci dprintk("2 N: %d, F: %lld, R: %d\n", N, (long long)F, R); 3418c2ecf20Sopenharmony_ci F -= (N + 32) * 262144; 3428c2ecf20Sopenharmony_ci 3438c2ecf20Sopenharmony_ci dprintk("3 N: %d, F: %lld, R: %d\n", N, (long long)F, R); 3448c2ecf20Sopenharmony_ci 3458c2ecf20Sopenharmony_ci if (state->Fwindow_enabled) { 3468c2ecf20Sopenharmony_ci if (F > (262144 / 2 - 1638)) 3478c2ecf20Sopenharmony_ci F = 262144 / 2 - 1638; 3488c2ecf20Sopenharmony_ci if (F < (-262144 / 2 + 1638)) 3498c2ecf20Sopenharmony_ci F = -262144 / 2 + 1638; 3508c2ecf20Sopenharmony_ci if ((F < 3277 && F > 0) || (F > -3277 && F < 0)) { 3518c2ecf20Sopenharmony_ci F = 0; 3528c2ecf20Sopenharmony_ci r = cx24113_readreg(state, 0x10); 3538c2ecf20Sopenharmony_ci cx24113_writereg(state, 0x10, r | (1 << 6)); 3548c2ecf20Sopenharmony_ci } 3558c2ecf20Sopenharmony_ci } 3568c2ecf20Sopenharmony_ci dprintk("4 N: %d, F: %lld, R: %d\n", N, (long long)F, R); 3578c2ecf20Sopenharmony_ci 3588c2ecf20Sopenharmony_ci *n = (u16) N; 3598c2ecf20Sopenharmony_ci *f = (s32) F; 3608c2ecf20Sopenharmony_ci} 3618c2ecf20Sopenharmony_ci 3628c2ecf20Sopenharmony_ci 3638c2ecf20Sopenharmony_cistatic void cx24113_set_nfr(struct cx24113_state *state, u16 n, s32 f, u8 r) 3648c2ecf20Sopenharmony_ci{ 3658c2ecf20Sopenharmony_ci u8 reg; 3668c2ecf20Sopenharmony_ci cx24113_writereg(state, 0x19, (n >> 1) & 0xff); 3678c2ecf20Sopenharmony_ci 3688c2ecf20Sopenharmony_ci reg = ((n & 0x1) << 7) | ((f >> 11) & 0x7f); 3698c2ecf20Sopenharmony_ci cx24113_writereg(state, 0x1a, reg); 3708c2ecf20Sopenharmony_ci 3718c2ecf20Sopenharmony_ci cx24113_writereg(state, 0x1b, (f >> 3) & 0xff); 3728c2ecf20Sopenharmony_ci 3738c2ecf20Sopenharmony_ci reg = cx24113_readreg(state, 0x1c) & 0x1f; 3748c2ecf20Sopenharmony_ci cx24113_writereg(state, 0x1c, reg | ((f & 0x7) << 5)); 3758c2ecf20Sopenharmony_ci 3768c2ecf20Sopenharmony_ci cx24113_set_Fref(state, r - 1); 3778c2ecf20Sopenharmony_ci} 3788c2ecf20Sopenharmony_ci 3798c2ecf20Sopenharmony_cistatic int cx24113_set_frequency(struct cx24113_state *state, u32 frequency) 3808c2ecf20Sopenharmony_ci{ 3818c2ecf20Sopenharmony_ci u8 r = 1; /* or 2 */ 3828c2ecf20Sopenharmony_ci u16 n = 6; 3838c2ecf20Sopenharmony_ci s32 f = 0; 3848c2ecf20Sopenharmony_ci 3858c2ecf20Sopenharmony_ci r = cx24113_readreg(state, 0x14); 3868c2ecf20Sopenharmony_ci cx24113_writereg(state, 0x14, r & 0x3f); 3878c2ecf20Sopenharmony_ci 3888c2ecf20Sopenharmony_ci r = cx24113_readreg(state, 0x10); 3898c2ecf20Sopenharmony_ci cx24113_writereg(state, 0x10, r & 0xbf); 3908c2ecf20Sopenharmony_ci 3918c2ecf20Sopenharmony_ci state->frequency = frequency; 3928c2ecf20Sopenharmony_ci 3938c2ecf20Sopenharmony_ci dprintk("tuning to frequency: %d\n", frequency); 3948c2ecf20Sopenharmony_ci 3958c2ecf20Sopenharmony_ci cx24113_calc_pll_nf(state, &n, &f); 3968c2ecf20Sopenharmony_ci cx24113_set_nfr(state, n, f, state->refdiv); 3978c2ecf20Sopenharmony_ci 3988c2ecf20Sopenharmony_ci r = cx24113_readreg(state, 0x18) & 0xbf; 3998c2ecf20Sopenharmony_ci if (state->vcodiv != VCODIV2) 4008c2ecf20Sopenharmony_ci r |= 1 << 6; 4018c2ecf20Sopenharmony_ci cx24113_writereg(state, 0x18, r); 4028c2ecf20Sopenharmony_ci 4038c2ecf20Sopenharmony_ci /* The need for this sleep is not clear. But helps in some cases */ 4048c2ecf20Sopenharmony_ci msleep(5); 4058c2ecf20Sopenharmony_ci 4068c2ecf20Sopenharmony_ci r = cx24113_readreg(state, 0x1c) & 0xef; 4078c2ecf20Sopenharmony_ci cx24113_writereg(state, 0x1c, r | (1 << 4)); 4088c2ecf20Sopenharmony_ci return 0; 4098c2ecf20Sopenharmony_ci} 4108c2ecf20Sopenharmony_ci 4118c2ecf20Sopenharmony_cistatic int cx24113_init(struct dvb_frontend *fe) 4128c2ecf20Sopenharmony_ci{ 4138c2ecf20Sopenharmony_ci struct cx24113_state *state = fe->tuner_priv; 4148c2ecf20Sopenharmony_ci int ret; 4158c2ecf20Sopenharmony_ci 4168c2ecf20Sopenharmony_ci state->tuner_gain_thres = -50; 4178c2ecf20Sopenharmony_ci state->gain_level = 255; /* to force a gain-setting initialization */ 4188c2ecf20Sopenharmony_ci state->icp_mode = 0; 4198c2ecf20Sopenharmony_ci 4208c2ecf20Sopenharmony_ci if (state->config->xtal_khz < 11000) { 4218c2ecf20Sopenharmony_ci state->icp_auto_hi = ICP_LEVEL4; 4228c2ecf20Sopenharmony_ci state->icp_auto_mhi = ICP_LEVEL4; 4238c2ecf20Sopenharmony_ci state->icp_auto_mlow = ICP_LEVEL3; 4248c2ecf20Sopenharmony_ci state->icp_auto_low = ICP_LEVEL3; 4258c2ecf20Sopenharmony_ci } else { 4268c2ecf20Sopenharmony_ci state->icp_auto_hi = ICP_LEVEL4; 4278c2ecf20Sopenharmony_ci state->icp_auto_mhi = ICP_LEVEL4; 4288c2ecf20Sopenharmony_ci state->icp_auto_mlow = ICP_LEVEL3; 4298c2ecf20Sopenharmony_ci state->icp_auto_low = ICP_LEVEL2; 4308c2ecf20Sopenharmony_ci } 4318c2ecf20Sopenharmony_ci 4328c2ecf20Sopenharmony_ci state->icp_dig = ICP_LEVEL3; 4338c2ecf20Sopenharmony_ci state->icp_man = ICP_LEVEL1; 4348c2ecf20Sopenharmony_ci state->acp_on = 1; 4358c2ecf20Sopenharmony_ci state->vco_mode = 0; 4368c2ecf20Sopenharmony_ci state->vco_shift = 0; 4378c2ecf20Sopenharmony_ci state->vco_band = VCOBANDSEL_1; 4388c2ecf20Sopenharmony_ci state->bs_delay = 8; 4398c2ecf20Sopenharmony_ci state->bs_freqcnt = 0x0fff; 4408c2ecf20Sopenharmony_ci state->bs_rdiv = 0x0fff; 4418c2ecf20Sopenharmony_ci state->prescaler_mode = 0; 4428c2ecf20Sopenharmony_ci state->lna_gain = LNA_MAX_GAIN; 4438c2ecf20Sopenharmony_ci state->rfvga_bias_ctrl = 1; 4448c2ecf20Sopenharmony_ci state->Fwindow_enabled = 1; 4458c2ecf20Sopenharmony_ci 4468c2ecf20Sopenharmony_ci cx24113_set_Fref(state, 0); 4478c2ecf20Sopenharmony_ci cx24113_enable(state, 0x3d); 4488c2ecf20Sopenharmony_ci cx24113_set_parameters(state); 4498c2ecf20Sopenharmony_ci 4508c2ecf20Sopenharmony_ci cx24113_set_gain_settings(state, -30); 4518c2ecf20Sopenharmony_ci 4528c2ecf20Sopenharmony_ci cx24113_set_bandwidth(state, 18025); 4538c2ecf20Sopenharmony_ci cx24113_set_clk_inversion(state, 1); 4548c2ecf20Sopenharmony_ci 4558c2ecf20Sopenharmony_ci if (state->config->xtal_khz >= 40000) 4568c2ecf20Sopenharmony_ci ret = cx24113_writereg(state, 0x02, 4578c2ecf20Sopenharmony_ci (cx24113_readreg(state, 0x02) & 0xfb) | (1 << 2)); 4588c2ecf20Sopenharmony_ci else 4598c2ecf20Sopenharmony_ci ret = cx24113_writereg(state, 0x02, 4608c2ecf20Sopenharmony_ci (cx24113_readreg(state, 0x02) & 0xfb) | (0 << 2)); 4618c2ecf20Sopenharmony_ci 4628c2ecf20Sopenharmony_ci return ret; 4638c2ecf20Sopenharmony_ci} 4648c2ecf20Sopenharmony_ci 4658c2ecf20Sopenharmony_cistatic int cx24113_set_params(struct dvb_frontend *fe) 4668c2ecf20Sopenharmony_ci{ 4678c2ecf20Sopenharmony_ci struct dtv_frontend_properties *c = &fe->dtv_property_cache; 4688c2ecf20Sopenharmony_ci struct cx24113_state *state = fe->tuner_priv; 4698c2ecf20Sopenharmony_ci /* for a ROLL-OFF factor of 0.35, 0.2: 600, 0.25: 625 */ 4708c2ecf20Sopenharmony_ci u32 roll_off = 675; 4718c2ecf20Sopenharmony_ci u32 bw; 4728c2ecf20Sopenharmony_ci 4738c2ecf20Sopenharmony_ci bw = ((c->symbol_rate/100) * roll_off) / 1000; 4748c2ecf20Sopenharmony_ci bw += (10000000/100) + 5; 4758c2ecf20Sopenharmony_ci bw /= 10; 4768c2ecf20Sopenharmony_ci bw += 1000; 4778c2ecf20Sopenharmony_ci cx24113_set_bandwidth(state, bw); 4788c2ecf20Sopenharmony_ci 4798c2ecf20Sopenharmony_ci cx24113_set_frequency(state, c->frequency); 4808c2ecf20Sopenharmony_ci msleep(5); 4818c2ecf20Sopenharmony_ci return cx24113_get_status(fe, &bw); 4828c2ecf20Sopenharmony_ci} 4838c2ecf20Sopenharmony_ci 4848c2ecf20Sopenharmony_cistatic s8 cx24113_agc_table[2][10] = { 4858c2ecf20Sopenharmony_ci {-54, -41, -35, -30, -25, -21, -16, -10, -6, -2}, 4868c2ecf20Sopenharmony_ci {-39, -35, -30, -25, -19, -15, -11, -5, 1, 9}, 4878c2ecf20Sopenharmony_ci}; 4888c2ecf20Sopenharmony_ci 4898c2ecf20Sopenharmony_civoid cx24113_agc_callback(struct dvb_frontend *fe) 4908c2ecf20Sopenharmony_ci{ 4918c2ecf20Sopenharmony_ci struct cx24113_state *state = fe->tuner_priv; 4928c2ecf20Sopenharmony_ci s16 s, i; 4938c2ecf20Sopenharmony_ci if (!fe->ops.read_signal_strength) 4948c2ecf20Sopenharmony_ci return; 4958c2ecf20Sopenharmony_ci 4968c2ecf20Sopenharmony_ci do { 4978c2ecf20Sopenharmony_ci /* this only works with the current CX24123 implementation */ 4988c2ecf20Sopenharmony_ci fe->ops.read_signal_strength(fe, (u16 *) &s); 4998c2ecf20Sopenharmony_ci s >>= 8; 5008c2ecf20Sopenharmony_ci dprintk("signal strength: %d\n", s); 5018c2ecf20Sopenharmony_ci for (i = 0; i < sizeof(cx24113_agc_table[0]); i++) 5028c2ecf20Sopenharmony_ci if (cx24113_agc_table[state->gain_level][i] > s) 5038c2ecf20Sopenharmony_ci break; 5048c2ecf20Sopenharmony_ci s = -25 - i*5; 5058c2ecf20Sopenharmony_ci } while (cx24113_set_gain_settings(state, s)); 5068c2ecf20Sopenharmony_ci} 5078c2ecf20Sopenharmony_ciEXPORT_SYMBOL(cx24113_agc_callback); 5088c2ecf20Sopenharmony_ci 5098c2ecf20Sopenharmony_cistatic int cx24113_get_frequency(struct dvb_frontend *fe, u32 *frequency) 5108c2ecf20Sopenharmony_ci{ 5118c2ecf20Sopenharmony_ci struct cx24113_state *state = fe->tuner_priv; 5128c2ecf20Sopenharmony_ci *frequency = state->frequency; 5138c2ecf20Sopenharmony_ci return 0; 5148c2ecf20Sopenharmony_ci} 5158c2ecf20Sopenharmony_ci 5168c2ecf20Sopenharmony_cistatic void cx24113_release(struct dvb_frontend *fe) 5178c2ecf20Sopenharmony_ci{ 5188c2ecf20Sopenharmony_ci struct cx24113_state *state = fe->tuner_priv; 5198c2ecf20Sopenharmony_ci dprintk("\n"); 5208c2ecf20Sopenharmony_ci fe->tuner_priv = NULL; 5218c2ecf20Sopenharmony_ci kfree(state); 5228c2ecf20Sopenharmony_ci} 5238c2ecf20Sopenharmony_ci 5248c2ecf20Sopenharmony_cistatic const struct dvb_tuner_ops cx24113_tuner_ops = { 5258c2ecf20Sopenharmony_ci .info = { 5268c2ecf20Sopenharmony_ci .name = "Conexant CX24113", 5278c2ecf20Sopenharmony_ci .frequency_min_hz = 950 * MHz, 5288c2ecf20Sopenharmony_ci .frequency_max_hz = 2150 * MHz, 5298c2ecf20Sopenharmony_ci .frequency_step_hz = 125 * kHz, 5308c2ecf20Sopenharmony_ci }, 5318c2ecf20Sopenharmony_ci 5328c2ecf20Sopenharmony_ci .release = cx24113_release, 5338c2ecf20Sopenharmony_ci 5348c2ecf20Sopenharmony_ci .init = cx24113_init, 5358c2ecf20Sopenharmony_ci 5368c2ecf20Sopenharmony_ci .set_params = cx24113_set_params, 5378c2ecf20Sopenharmony_ci .get_frequency = cx24113_get_frequency, 5388c2ecf20Sopenharmony_ci .get_status = cx24113_get_status, 5398c2ecf20Sopenharmony_ci}; 5408c2ecf20Sopenharmony_ci 5418c2ecf20Sopenharmony_cistruct dvb_frontend *cx24113_attach(struct dvb_frontend *fe, 5428c2ecf20Sopenharmony_ci const struct cx24113_config *config, struct i2c_adapter *i2c) 5438c2ecf20Sopenharmony_ci{ 5448c2ecf20Sopenharmony_ci /* allocate memory for the internal state */ 5458c2ecf20Sopenharmony_ci struct cx24113_state *state = kzalloc(sizeof(*state), GFP_KERNEL); 5468c2ecf20Sopenharmony_ci int rc; 5478c2ecf20Sopenharmony_ci 5488c2ecf20Sopenharmony_ci if (!state) 5498c2ecf20Sopenharmony_ci return NULL; 5508c2ecf20Sopenharmony_ci 5518c2ecf20Sopenharmony_ci /* setup the state */ 5528c2ecf20Sopenharmony_ci state->config = config; 5538c2ecf20Sopenharmony_ci state->i2c = i2c; 5548c2ecf20Sopenharmony_ci 5558c2ecf20Sopenharmony_ci cx_info("trying to detect myself\n"); 5568c2ecf20Sopenharmony_ci 5578c2ecf20Sopenharmony_ci /* making a dummy read, because of some expected troubles 5588c2ecf20Sopenharmony_ci * after power on */ 5598c2ecf20Sopenharmony_ci cx24113_readreg(state, 0x00); 5608c2ecf20Sopenharmony_ci 5618c2ecf20Sopenharmony_ci rc = cx24113_readreg(state, 0x00); 5628c2ecf20Sopenharmony_ci if (rc < 0) { 5638c2ecf20Sopenharmony_ci cx_info("CX24113 not found.\n"); 5648c2ecf20Sopenharmony_ci goto error; 5658c2ecf20Sopenharmony_ci } 5668c2ecf20Sopenharmony_ci state->rev = rc; 5678c2ecf20Sopenharmony_ci 5688c2ecf20Sopenharmony_ci switch (rc) { 5698c2ecf20Sopenharmony_ci case 0x43: 5708c2ecf20Sopenharmony_ci cx_info("detected CX24113 variant\n"); 5718c2ecf20Sopenharmony_ci break; 5728c2ecf20Sopenharmony_ci case REV_CX24113: 5738c2ecf20Sopenharmony_ci cx_info("successfully detected\n"); 5748c2ecf20Sopenharmony_ci break; 5758c2ecf20Sopenharmony_ci default: 5768c2ecf20Sopenharmony_ci cx_err("unsupported device id: %x\n", state->rev); 5778c2ecf20Sopenharmony_ci goto error; 5788c2ecf20Sopenharmony_ci } 5798c2ecf20Sopenharmony_ci state->ver = cx24113_readreg(state, 0x01); 5808c2ecf20Sopenharmony_ci cx_info("version: %x\n", state->ver); 5818c2ecf20Sopenharmony_ci 5828c2ecf20Sopenharmony_ci /* create dvb_frontend */ 5838c2ecf20Sopenharmony_ci memcpy(&fe->ops.tuner_ops, &cx24113_tuner_ops, 5848c2ecf20Sopenharmony_ci sizeof(struct dvb_tuner_ops)); 5858c2ecf20Sopenharmony_ci fe->tuner_priv = state; 5868c2ecf20Sopenharmony_ci return fe; 5878c2ecf20Sopenharmony_ci 5888c2ecf20Sopenharmony_cierror: 5898c2ecf20Sopenharmony_ci kfree(state); 5908c2ecf20Sopenharmony_ci 5918c2ecf20Sopenharmony_ci return NULL; 5928c2ecf20Sopenharmony_ci} 5938c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(cx24113_attach); 5948c2ecf20Sopenharmony_ci 5958c2ecf20Sopenharmony_cimodule_param(debug, int, 0644); 5968c2ecf20Sopenharmony_ciMODULE_PARM_DESC(debug, "Activates frontend debugging (default:0)"); 5978c2ecf20Sopenharmony_ci 5988c2ecf20Sopenharmony_ciMODULE_AUTHOR("Patrick Boettcher <pb@linuxtv.org>"); 5998c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("DVB Frontend module for Conexant CX24113/CX24128hardware"); 6008c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 6018c2ecf20Sopenharmony_ci 602