xref: /OK3568_Linux_fs/kernel/drivers/media/dvb-frontends/cx24113.c (revision 4882a59341e53eb6f0b4789bf948001014eff981)
1*4882a593Smuzhiyun // SPDX-License-Identifier: GPL-2.0-or-later
2*4882a593Smuzhiyun /*
3*4882a593Smuzhiyun  *  Driver for Conexant CX24113/CX24128 Tuner (Satellite)
4*4882a593Smuzhiyun  *
5*4882a593Smuzhiyun  *  Copyright (C) 2007-8 Patrick Boettcher <pb@linuxtv.org>
6*4882a593Smuzhiyun  *
7*4882a593Smuzhiyun  *  Developed for BBTI / Technisat
8*4882a593Smuzhiyun  */
9*4882a593Smuzhiyun 
10*4882a593Smuzhiyun #include <linux/slab.h>
11*4882a593Smuzhiyun #include <linux/kernel.h>
12*4882a593Smuzhiyun #include <linux/module.h>
13*4882a593Smuzhiyun #include <linux/init.h>
14*4882a593Smuzhiyun 
15*4882a593Smuzhiyun #include <media/dvb_frontend.h>
16*4882a593Smuzhiyun #include "cx24113.h"
17*4882a593Smuzhiyun 
18*4882a593Smuzhiyun static int debug;
19*4882a593Smuzhiyun 
20*4882a593Smuzhiyun #define cx_info(args...) do { printk(KERN_INFO "CX24113: " args); } while (0)
21*4882a593Smuzhiyun #define cx_err(args...)  do { printk(KERN_ERR  "CX24113: " args); } while (0)
22*4882a593Smuzhiyun 
23*4882a593Smuzhiyun #define dprintk(args...) \
24*4882a593Smuzhiyun 	do { \
25*4882a593Smuzhiyun 		if (debug) { \
26*4882a593Smuzhiyun 			printk(KERN_DEBUG "CX24113: %s: ", __func__); \
27*4882a593Smuzhiyun 			printk(args); \
28*4882a593Smuzhiyun 		} \
29*4882a593Smuzhiyun 	} while (0)
30*4882a593Smuzhiyun 
31*4882a593Smuzhiyun struct cx24113_state {
32*4882a593Smuzhiyun 	struct i2c_adapter *i2c;
33*4882a593Smuzhiyun 	const struct cx24113_config *config;
34*4882a593Smuzhiyun 
35*4882a593Smuzhiyun #define REV_CX24113 0x23
36*4882a593Smuzhiyun 	u8 rev;
37*4882a593Smuzhiyun 	u8 ver;
38*4882a593Smuzhiyun 
39*4882a593Smuzhiyun 	u8 icp_mode:1;
40*4882a593Smuzhiyun 
41*4882a593Smuzhiyun #define ICP_LEVEL1 0
42*4882a593Smuzhiyun #define ICP_LEVEL2 1
43*4882a593Smuzhiyun #define ICP_LEVEL3 2
44*4882a593Smuzhiyun #define ICP_LEVEL4 3
45*4882a593Smuzhiyun 	u8 icp_man:2;
46*4882a593Smuzhiyun 	u8 icp_auto_low:2;
47*4882a593Smuzhiyun 	u8 icp_auto_mlow:2;
48*4882a593Smuzhiyun 	u8 icp_auto_mhi:2;
49*4882a593Smuzhiyun 	u8 icp_auto_hi:2;
50*4882a593Smuzhiyun 	u8 icp_dig;
51*4882a593Smuzhiyun 
52*4882a593Smuzhiyun #define LNA_MIN_GAIN 0
53*4882a593Smuzhiyun #define LNA_MID_GAIN 1
54*4882a593Smuzhiyun #define LNA_MAX_GAIN 2
55*4882a593Smuzhiyun 	u8 lna_gain:2;
56*4882a593Smuzhiyun 
57*4882a593Smuzhiyun 	u8 acp_on:1;
58*4882a593Smuzhiyun 
59*4882a593Smuzhiyun 	u8 vco_mode:2;
60*4882a593Smuzhiyun 	u8 vco_shift:1;
61*4882a593Smuzhiyun #define VCOBANDSEL_6 0x80
62*4882a593Smuzhiyun #define VCOBANDSEL_5 0x01
63*4882a593Smuzhiyun #define VCOBANDSEL_4 0x02
64*4882a593Smuzhiyun #define VCOBANDSEL_3 0x04
65*4882a593Smuzhiyun #define VCOBANDSEL_2 0x08
66*4882a593Smuzhiyun #define VCOBANDSEL_1 0x10
67*4882a593Smuzhiyun 	u8 vco_band;
68*4882a593Smuzhiyun 
69*4882a593Smuzhiyun #define VCODIV4 4
70*4882a593Smuzhiyun #define VCODIV2 2
71*4882a593Smuzhiyun 	u8 vcodiv;
72*4882a593Smuzhiyun 
73*4882a593Smuzhiyun 	u8 bs_delay:4;
74*4882a593Smuzhiyun 	u16 bs_freqcnt:13;
75*4882a593Smuzhiyun 	u16 bs_rdiv;
76*4882a593Smuzhiyun 	u8 prescaler_mode:1;
77*4882a593Smuzhiyun 
78*4882a593Smuzhiyun 	u8 rfvga_bias_ctrl;
79*4882a593Smuzhiyun 
80*4882a593Smuzhiyun 	s16 tuner_gain_thres;
81*4882a593Smuzhiyun 	u8  gain_level;
82*4882a593Smuzhiyun 
83*4882a593Smuzhiyun 	u32 frequency;
84*4882a593Smuzhiyun 
85*4882a593Smuzhiyun 	u8 refdiv;
86*4882a593Smuzhiyun 
87*4882a593Smuzhiyun 	u8 Fwindow_enabled;
88*4882a593Smuzhiyun };
89*4882a593Smuzhiyun 
cx24113_writereg(struct cx24113_state * state,int reg,int data)90*4882a593Smuzhiyun static int cx24113_writereg(struct cx24113_state *state, int reg, int data)
91*4882a593Smuzhiyun {
92*4882a593Smuzhiyun 	u8 buf[] = { reg, data };
93*4882a593Smuzhiyun 	struct i2c_msg msg = { .addr = state->config->i2c_addr,
94*4882a593Smuzhiyun 		.flags = 0, .buf = buf, .len = 2 };
95*4882a593Smuzhiyun 	int err = i2c_transfer(state->i2c, &msg, 1);
96*4882a593Smuzhiyun 	if (err != 1) {
97*4882a593Smuzhiyun 		printk(KERN_DEBUG "%s: writereg error(err == %i, reg == 0x%02x, data == 0x%02x)\n",
98*4882a593Smuzhiyun 		       __func__, err, reg, data);
99*4882a593Smuzhiyun 		return err;
100*4882a593Smuzhiyun 	}
101*4882a593Smuzhiyun 
102*4882a593Smuzhiyun 	return 0;
103*4882a593Smuzhiyun }
104*4882a593Smuzhiyun 
cx24113_readreg(struct cx24113_state * state,u8 reg)105*4882a593Smuzhiyun static int cx24113_readreg(struct cx24113_state *state, u8 reg)
106*4882a593Smuzhiyun {
107*4882a593Smuzhiyun 	int ret;
108*4882a593Smuzhiyun 	u8 b;
109*4882a593Smuzhiyun 	struct i2c_msg msg[] = {
110*4882a593Smuzhiyun 		{ .addr = state->config->i2c_addr,
111*4882a593Smuzhiyun 			.flags = 0, .buf = &reg, .len = 1 },
112*4882a593Smuzhiyun 		{ .addr = state->config->i2c_addr,
113*4882a593Smuzhiyun 			.flags = I2C_M_RD, .buf = &b, .len = 1 }
114*4882a593Smuzhiyun 	};
115*4882a593Smuzhiyun 
116*4882a593Smuzhiyun 	ret = i2c_transfer(state->i2c, msg, 2);
117*4882a593Smuzhiyun 
118*4882a593Smuzhiyun 	if (ret != 2) {
119*4882a593Smuzhiyun 		printk(KERN_DEBUG "%s: reg=0x%x (error=%d)\n",
120*4882a593Smuzhiyun 			__func__, reg, ret);
121*4882a593Smuzhiyun 		return ret;
122*4882a593Smuzhiyun 	}
123*4882a593Smuzhiyun 
124*4882a593Smuzhiyun 	return b;
125*4882a593Smuzhiyun }
126*4882a593Smuzhiyun 
cx24113_set_parameters(struct cx24113_state * state)127*4882a593Smuzhiyun static void cx24113_set_parameters(struct cx24113_state *state)
128*4882a593Smuzhiyun {
129*4882a593Smuzhiyun 	u8 r;
130*4882a593Smuzhiyun 
131*4882a593Smuzhiyun 	r = cx24113_readreg(state, 0x10) & 0x82;
132*4882a593Smuzhiyun 	r |= state->icp_mode;
133*4882a593Smuzhiyun 	r |= state->icp_man << 4;
134*4882a593Smuzhiyun 	r |= state->icp_dig << 2;
135*4882a593Smuzhiyun 	r |= state->prescaler_mode << 5;
136*4882a593Smuzhiyun 	cx24113_writereg(state, 0x10, r);
137*4882a593Smuzhiyun 
138*4882a593Smuzhiyun 	r = (state->icp_auto_low  << 0) | (state->icp_auto_mlow << 2)
139*4882a593Smuzhiyun 		| (state->icp_auto_mhi << 4) | (state->icp_auto_hi << 6);
140*4882a593Smuzhiyun 	cx24113_writereg(state, 0x11, r);
141*4882a593Smuzhiyun 
142*4882a593Smuzhiyun 	if (state->rev == REV_CX24113) {
143*4882a593Smuzhiyun 		r = cx24113_readreg(state, 0x20) & 0xec;
144*4882a593Smuzhiyun 		r |= state->lna_gain;
145*4882a593Smuzhiyun 		r |= state->rfvga_bias_ctrl << 4;
146*4882a593Smuzhiyun 		cx24113_writereg(state, 0x20, r);
147*4882a593Smuzhiyun 	}
148*4882a593Smuzhiyun 
149*4882a593Smuzhiyun 	r = cx24113_readreg(state, 0x12) & 0x03;
150*4882a593Smuzhiyun 	r |= state->acp_on << 2;
151*4882a593Smuzhiyun 	r |= state->bs_delay << 4;
152*4882a593Smuzhiyun 	cx24113_writereg(state, 0x12, r);
153*4882a593Smuzhiyun 
154*4882a593Smuzhiyun 	r = cx24113_readreg(state, 0x18) & 0x40;
155*4882a593Smuzhiyun 	r |= state->vco_shift;
156*4882a593Smuzhiyun 	if (state->vco_band == VCOBANDSEL_6)
157*4882a593Smuzhiyun 		r |= (1 << 7);
158*4882a593Smuzhiyun 	else
159*4882a593Smuzhiyun 		r |= (state->vco_band << 1);
160*4882a593Smuzhiyun 	cx24113_writereg(state, 0x18, r);
161*4882a593Smuzhiyun 
162*4882a593Smuzhiyun 	r  = cx24113_readreg(state, 0x14) & 0x20;
163*4882a593Smuzhiyun 	r |= (state->vco_mode << 6) | ((state->bs_freqcnt >> 8) & 0x1f);
164*4882a593Smuzhiyun 	cx24113_writereg(state, 0x14, r);
165*4882a593Smuzhiyun 	cx24113_writereg(state, 0x15, (state->bs_freqcnt        & 0xff));
166*4882a593Smuzhiyun 
167*4882a593Smuzhiyun 	cx24113_writereg(state, 0x16, (state->bs_rdiv >> 4) & 0xff);
168*4882a593Smuzhiyun 	r = (cx24113_readreg(state, 0x17) & 0x0f) |
169*4882a593Smuzhiyun 		((state->bs_rdiv & 0x0f) << 4);
170*4882a593Smuzhiyun 	cx24113_writereg(state, 0x17, r);
171*4882a593Smuzhiyun }
172*4882a593Smuzhiyun 
173*4882a593Smuzhiyun #define VGA_0 0x00
174*4882a593Smuzhiyun #define VGA_1 0x04
175*4882a593Smuzhiyun #define VGA_2 0x02
176*4882a593Smuzhiyun #define VGA_3 0x06
177*4882a593Smuzhiyun #define VGA_4 0x01
178*4882a593Smuzhiyun #define VGA_5 0x05
179*4882a593Smuzhiyun #define VGA_6 0x03
180*4882a593Smuzhiyun #define VGA_7 0x07
181*4882a593Smuzhiyun 
182*4882a593Smuzhiyun #define RFVGA_0 0x00
183*4882a593Smuzhiyun #define RFVGA_1 0x01
184*4882a593Smuzhiyun #define RFVGA_2 0x02
185*4882a593Smuzhiyun #define RFVGA_3 0x03
186*4882a593Smuzhiyun 
cx24113_set_gain_settings(struct cx24113_state * state,s16 power_estimation)187*4882a593Smuzhiyun static int cx24113_set_gain_settings(struct cx24113_state *state,
188*4882a593Smuzhiyun 		s16 power_estimation)
189*4882a593Smuzhiyun {
190*4882a593Smuzhiyun 	u8 ampout = cx24113_readreg(state, 0x1d) & 0xf0,
191*4882a593Smuzhiyun 	   vga    = cx24113_readreg(state, 0x1f) & 0x3f,
192*4882a593Smuzhiyun 	   rfvga  = cx24113_readreg(state, 0x20) & 0xf3;
193*4882a593Smuzhiyun 	u8 gain_level = power_estimation >= state->tuner_gain_thres;
194*4882a593Smuzhiyun 
195*4882a593Smuzhiyun 	dprintk("power estimation: %d, thres: %d, gain_level: %d/%d\n",
196*4882a593Smuzhiyun 			power_estimation, state->tuner_gain_thres,
197*4882a593Smuzhiyun 			state->gain_level, gain_level);
198*4882a593Smuzhiyun 
199*4882a593Smuzhiyun 	if (gain_level == state->gain_level)
200*4882a593Smuzhiyun 		return 0; /* nothing to be done */
201*4882a593Smuzhiyun 
202*4882a593Smuzhiyun 	ampout |= 0xf;
203*4882a593Smuzhiyun 
204*4882a593Smuzhiyun 	if (gain_level) {
205*4882a593Smuzhiyun 		rfvga |= RFVGA_0 << 2;
206*4882a593Smuzhiyun 		vga   |= (VGA_7 << 3) | VGA_7;
207*4882a593Smuzhiyun 	} else {
208*4882a593Smuzhiyun 		rfvga |= RFVGA_2 << 2;
209*4882a593Smuzhiyun 		vga  |= (VGA_6 << 3) | VGA_2;
210*4882a593Smuzhiyun 	}
211*4882a593Smuzhiyun 	state->gain_level = gain_level;
212*4882a593Smuzhiyun 
213*4882a593Smuzhiyun 	cx24113_writereg(state, 0x1d, ampout);
214*4882a593Smuzhiyun 	cx24113_writereg(state, 0x1f, vga);
215*4882a593Smuzhiyun 	cx24113_writereg(state, 0x20, rfvga);
216*4882a593Smuzhiyun 
217*4882a593Smuzhiyun 	return 1; /* did something */
218*4882a593Smuzhiyun }
219*4882a593Smuzhiyun 
cx24113_set_Fref(struct cx24113_state * state,u8 high)220*4882a593Smuzhiyun static int cx24113_set_Fref(struct cx24113_state *state, u8 high)
221*4882a593Smuzhiyun {
222*4882a593Smuzhiyun 	u8 xtal = cx24113_readreg(state, 0x02);
223*4882a593Smuzhiyun 	if (state->rev == 0x43 && state->vcodiv == VCODIV4)
224*4882a593Smuzhiyun 		high = 1;
225*4882a593Smuzhiyun 
226*4882a593Smuzhiyun 	xtal &= ~0x2;
227*4882a593Smuzhiyun 	if (high)
228*4882a593Smuzhiyun 		xtal |= high << 1;
229*4882a593Smuzhiyun 	return cx24113_writereg(state, 0x02, xtal);
230*4882a593Smuzhiyun }
231*4882a593Smuzhiyun 
cx24113_enable(struct cx24113_state * state,u8 enable)232*4882a593Smuzhiyun static int cx24113_enable(struct cx24113_state *state, u8 enable)
233*4882a593Smuzhiyun {
234*4882a593Smuzhiyun 	u8 r21 = (cx24113_readreg(state, 0x21) & 0xc0) | enable;
235*4882a593Smuzhiyun 	if (state->rev == REV_CX24113)
236*4882a593Smuzhiyun 		r21 |= (1 << 1);
237*4882a593Smuzhiyun 	return cx24113_writereg(state, 0x21, r21);
238*4882a593Smuzhiyun }
239*4882a593Smuzhiyun 
cx24113_set_bandwidth(struct cx24113_state * state,u32 bandwidth_khz)240*4882a593Smuzhiyun static int cx24113_set_bandwidth(struct cx24113_state *state, u32 bandwidth_khz)
241*4882a593Smuzhiyun {
242*4882a593Smuzhiyun 	u8 r;
243*4882a593Smuzhiyun 
244*4882a593Smuzhiyun 	if (bandwidth_khz <= 19000)
245*4882a593Smuzhiyun 		r = 0x03 << 6;
246*4882a593Smuzhiyun 	else if (bandwidth_khz <= 25000)
247*4882a593Smuzhiyun 		r = 0x02 << 6;
248*4882a593Smuzhiyun 	else
249*4882a593Smuzhiyun 		r = 0x01 << 6;
250*4882a593Smuzhiyun 
251*4882a593Smuzhiyun 	dprintk("bandwidth to be set: %d\n", bandwidth_khz);
252*4882a593Smuzhiyun 	bandwidth_khz *= 10;
253*4882a593Smuzhiyun 	bandwidth_khz -= 10000;
254*4882a593Smuzhiyun 	bandwidth_khz /= 1000;
255*4882a593Smuzhiyun 	bandwidth_khz += 5;
256*4882a593Smuzhiyun 	bandwidth_khz /= 10;
257*4882a593Smuzhiyun 
258*4882a593Smuzhiyun 	dprintk("bandwidth: %d %d\n", r >> 6, bandwidth_khz);
259*4882a593Smuzhiyun 
260*4882a593Smuzhiyun 	r |= bandwidth_khz & 0x3f;
261*4882a593Smuzhiyun 
262*4882a593Smuzhiyun 	return cx24113_writereg(state, 0x1e, r);
263*4882a593Smuzhiyun }
264*4882a593Smuzhiyun 
cx24113_set_clk_inversion(struct cx24113_state * state,u8 on)265*4882a593Smuzhiyun static int cx24113_set_clk_inversion(struct cx24113_state *state, u8 on)
266*4882a593Smuzhiyun {
267*4882a593Smuzhiyun 	u8 r = (cx24113_readreg(state, 0x10) & 0x7f) | ((on & 0x1) << 7);
268*4882a593Smuzhiyun 	return cx24113_writereg(state, 0x10, r);
269*4882a593Smuzhiyun }
270*4882a593Smuzhiyun 
cx24113_get_status(struct dvb_frontend * fe,u32 * status)271*4882a593Smuzhiyun static int cx24113_get_status(struct dvb_frontend *fe, u32 *status)
272*4882a593Smuzhiyun {
273*4882a593Smuzhiyun 	struct cx24113_state *state = fe->tuner_priv;
274*4882a593Smuzhiyun 	u8 r = (cx24113_readreg(state, 0x10) & 0x02) >> 1;
275*4882a593Smuzhiyun 	if (r)
276*4882a593Smuzhiyun 		*status |= TUNER_STATUS_LOCKED;
277*4882a593Smuzhiyun 	dprintk("PLL locked: %d\n", r);
278*4882a593Smuzhiyun 	return 0;
279*4882a593Smuzhiyun }
280*4882a593Smuzhiyun 
cx24113_set_ref_div(struct cx24113_state * state,u8 refdiv)281*4882a593Smuzhiyun static u8 cx24113_set_ref_div(struct cx24113_state *state, u8 refdiv)
282*4882a593Smuzhiyun {
283*4882a593Smuzhiyun 	if (state->rev == 0x43 && state->vcodiv == VCODIV4)
284*4882a593Smuzhiyun 		refdiv = 2;
285*4882a593Smuzhiyun 	return state->refdiv = refdiv;
286*4882a593Smuzhiyun }
287*4882a593Smuzhiyun 
cx24113_calc_pll_nf(struct cx24113_state * state,u16 * n,s32 * f)288*4882a593Smuzhiyun static void cx24113_calc_pll_nf(struct cx24113_state *state, u16 *n, s32 *f)
289*4882a593Smuzhiyun {
290*4882a593Smuzhiyun 	s32 N;
291*4882a593Smuzhiyun 	s64 F;
292*4882a593Smuzhiyun 	u64 dividend;
293*4882a593Smuzhiyun 	u8 R, r;
294*4882a593Smuzhiyun 	u8 vcodiv;
295*4882a593Smuzhiyun 	u8 factor;
296*4882a593Smuzhiyun 	s32 freq_hz = state->frequency * 1000;
297*4882a593Smuzhiyun 
298*4882a593Smuzhiyun 	if (state->config->xtal_khz < 20000)
299*4882a593Smuzhiyun 		factor = 1;
300*4882a593Smuzhiyun 	else
301*4882a593Smuzhiyun 		factor = 2;
302*4882a593Smuzhiyun 
303*4882a593Smuzhiyun 	if (state->rev == REV_CX24113) {
304*4882a593Smuzhiyun 		if (state->frequency >= 1100000)
305*4882a593Smuzhiyun 			vcodiv = VCODIV2;
306*4882a593Smuzhiyun 		else
307*4882a593Smuzhiyun 			vcodiv = VCODIV4;
308*4882a593Smuzhiyun 	} else {
309*4882a593Smuzhiyun 		if (state->frequency >= 1165000)
310*4882a593Smuzhiyun 			vcodiv = VCODIV2;
311*4882a593Smuzhiyun 		else
312*4882a593Smuzhiyun 			vcodiv = VCODIV4;
313*4882a593Smuzhiyun 	}
314*4882a593Smuzhiyun 	state->vcodiv = vcodiv;
315*4882a593Smuzhiyun 
316*4882a593Smuzhiyun 	dprintk("calculating N/F for %dHz with vcodiv %d\n", freq_hz, vcodiv);
317*4882a593Smuzhiyun 	R = 0;
318*4882a593Smuzhiyun 	do {
319*4882a593Smuzhiyun 		R = cx24113_set_ref_div(state, R + 1);
320*4882a593Smuzhiyun 
321*4882a593Smuzhiyun 		/* calculate tuner PLL settings: */
322*4882a593Smuzhiyun 		N =  (freq_hz / 100 * vcodiv) * R;
323*4882a593Smuzhiyun 		N /= (state->config->xtal_khz) * factor * 2;
324*4882a593Smuzhiyun 		N += 5;     /* For round up. */
325*4882a593Smuzhiyun 		N /= 10;
326*4882a593Smuzhiyun 		N -= 32;
327*4882a593Smuzhiyun 	} while (N < 6 && R < 3);
328*4882a593Smuzhiyun 
329*4882a593Smuzhiyun 	if (N < 6) {
330*4882a593Smuzhiyun 		cx_err("strange frequency: N < 6\n");
331*4882a593Smuzhiyun 		return;
332*4882a593Smuzhiyun 	}
333*4882a593Smuzhiyun 	F = freq_hz;
334*4882a593Smuzhiyun 	F *= (u64) (R * vcodiv * 262144);
335*4882a593Smuzhiyun 	dprintk("1 N: %d, F: %lld, R: %d\n", N, (long long)F, R);
336*4882a593Smuzhiyun 	/* do_div needs an u64 as first argument */
337*4882a593Smuzhiyun 	dividend = F;
338*4882a593Smuzhiyun 	do_div(dividend, state->config->xtal_khz * 1000 * factor * 2);
339*4882a593Smuzhiyun 	F = dividend;
340*4882a593Smuzhiyun 	dprintk("2 N: %d, F: %lld, R: %d\n", N, (long long)F, R);
341*4882a593Smuzhiyun 	F -= (N + 32) * 262144;
342*4882a593Smuzhiyun 
343*4882a593Smuzhiyun 	dprintk("3 N: %d, F: %lld, R: %d\n", N, (long long)F, R);
344*4882a593Smuzhiyun 
345*4882a593Smuzhiyun 	if (state->Fwindow_enabled) {
346*4882a593Smuzhiyun 		if (F > (262144 / 2 - 1638))
347*4882a593Smuzhiyun 			F = 262144 / 2 - 1638;
348*4882a593Smuzhiyun 		if (F < (-262144 / 2 + 1638))
349*4882a593Smuzhiyun 			F = -262144 / 2 + 1638;
350*4882a593Smuzhiyun 		if ((F < 3277 && F > 0) || (F > -3277 && F < 0)) {
351*4882a593Smuzhiyun 			F = 0;
352*4882a593Smuzhiyun 			r = cx24113_readreg(state, 0x10);
353*4882a593Smuzhiyun 			cx24113_writereg(state, 0x10, r | (1 << 6));
354*4882a593Smuzhiyun 		}
355*4882a593Smuzhiyun 	}
356*4882a593Smuzhiyun 	dprintk("4 N: %d, F: %lld, R: %d\n", N, (long long)F, R);
357*4882a593Smuzhiyun 
358*4882a593Smuzhiyun 	*n = (u16) N;
359*4882a593Smuzhiyun 	*f = (s32) F;
360*4882a593Smuzhiyun }
361*4882a593Smuzhiyun 
362*4882a593Smuzhiyun 
cx24113_set_nfr(struct cx24113_state * state,u16 n,s32 f,u8 r)363*4882a593Smuzhiyun static void cx24113_set_nfr(struct cx24113_state *state, u16 n, s32 f, u8 r)
364*4882a593Smuzhiyun {
365*4882a593Smuzhiyun 	u8 reg;
366*4882a593Smuzhiyun 	cx24113_writereg(state, 0x19, (n >> 1) & 0xff);
367*4882a593Smuzhiyun 
368*4882a593Smuzhiyun 	reg = ((n & 0x1) << 7) | ((f >> 11) & 0x7f);
369*4882a593Smuzhiyun 	cx24113_writereg(state, 0x1a, reg);
370*4882a593Smuzhiyun 
371*4882a593Smuzhiyun 	cx24113_writereg(state, 0x1b, (f >> 3) & 0xff);
372*4882a593Smuzhiyun 
373*4882a593Smuzhiyun 	reg = cx24113_readreg(state, 0x1c) & 0x1f;
374*4882a593Smuzhiyun 	cx24113_writereg(state, 0x1c, reg | ((f & 0x7) << 5));
375*4882a593Smuzhiyun 
376*4882a593Smuzhiyun 	cx24113_set_Fref(state, r - 1);
377*4882a593Smuzhiyun }
378*4882a593Smuzhiyun 
cx24113_set_frequency(struct cx24113_state * state,u32 frequency)379*4882a593Smuzhiyun static int cx24113_set_frequency(struct cx24113_state *state, u32 frequency)
380*4882a593Smuzhiyun {
381*4882a593Smuzhiyun 	u8 r = 1; /* or 2 */
382*4882a593Smuzhiyun 	u16 n = 6;
383*4882a593Smuzhiyun 	s32 f = 0;
384*4882a593Smuzhiyun 
385*4882a593Smuzhiyun 	r = cx24113_readreg(state, 0x14);
386*4882a593Smuzhiyun 	cx24113_writereg(state, 0x14, r & 0x3f);
387*4882a593Smuzhiyun 
388*4882a593Smuzhiyun 	r = cx24113_readreg(state, 0x10);
389*4882a593Smuzhiyun 	cx24113_writereg(state, 0x10, r & 0xbf);
390*4882a593Smuzhiyun 
391*4882a593Smuzhiyun 	state->frequency = frequency;
392*4882a593Smuzhiyun 
393*4882a593Smuzhiyun 	dprintk("tuning to frequency: %d\n", frequency);
394*4882a593Smuzhiyun 
395*4882a593Smuzhiyun 	cx24113_calc_pll_nf(state, &n, &f);
396*4882a593Smuzhiyun 	cx24113_set_nfr(state, n, f, state->refdiv);
397*4882a593Smuzhiyun 
398*4882a593Smuzhiyun 	r = cx24113_readreg(state, 0x18) & 0xbf;
399*4882a593Smuzhiyun 	if (state->vcodiv != VCODIV2)
400*4882a593Smuzhiyun 		r |= 1 << 6;
401*4882a593Smuzhiyun 	cx24113_writereg(state, 0x18, r);
402*4882a593Smuzhiyun 
403*4882a593Smuzhiyun 	/* The need for this sleep is not clear. But helps in some cases */
404*4882a593Smuzhiyun 	msleep(5);
405*4882a593Smuzhiyun 
406*4882a593Smuzhiyun 	r = cx24113_readreg(state, 0x1c) & 0xef;
407*4882a593Smuzhiyun 	cx24113_writereg(state, 0x1c, r | (1 << 4));
408*4882a593Smuzhiyun 	return 0;
409*4882a593Smuzhiyun }
410*4882a593Smuzhiyun 
cx24113_init(struct dvb_frontend * fe)411*4882a593Smuzhiyun static int cx24113_init(struct dvb_frontend *fe)
412*4882a593Smuzhiyun {
413*4882a593Smuzhiyun 	struct cx24113_state *state = fe->tuner_priv;
414*4882a593Smuzhiyun 	int ret;
415*4882a593Smuzhiyun 
416*4882a593Smuzhiyun 	state->tuner_gain_thres = -50;
417*4882a593Smuzhiyun 	state->gain_level = 255; /* to force a gain-setting initialization */
418*4882a593Smuzhiyun 	state->icp_mode = 0;
419*4882a593Smuzhiyun 
420*4882a593Smuzhiyun 	if (state->config->xtal_khz < 11000) {
421*4882a593Smuzhiyun 		state->icp_auto_hi  = ICP_LEVEL4;
422*4882a593Smuzhiyun 		state->icp_auto_mhi  = ICP_LEVEL4;
423*4882a593Smuzhiyun 		state->icp_auto_mlow = ICP_LEVEL3;
424*4882a593Smuzhiyun 		state->icp_auto_low = ICP_LEVEL3;
425*4882a593Smuzhiyun 	} else {
426*4882a593Smuzhiyun 		state->icp_auto_hi  = ICP_LEVEL4;
427*4882a593Smuzhiyun 		state->icp_auto_mhi  = ICP_LEVEL4;
428*4882a593Smuzhiyun 		state->icp_auto_mlow = ICP_LEVEL3;
429*4882a593Smuzhiyun 		state->icp_auto_low = ICP_LEVEL2;
430*4882a593Smuzhiyun 	}
431*4882a593Smuzhiyun 
432*4882a593Smuzhiyun 	state->icp_dig = ICP_LEVEL3;
433*4882a593Smuzhiyun 	state->icp_man = ICP_LEVEL1;
434*4882a593Smuzhiyun 	state->acp_on  = 1;
435*4882a593Smuzhiyun 	state->vco_mode = 0;
436*4882a593Smuzhiyun 	state->vco_shift = 0;
437*4882a593Smuzhiyun 	state->vco_band = VCOBANDSEL_1;
438*4882a593Smuzhiyun 	state->bs_delay = 8;
439*4882a593Smuzhiyun 	state->bs_freqcnt = 0x0fff;
440*4882a593Smuzhiyun 	state->bs_rdiv = 0x0fff;
441*4882a593Smuzhiyun 	state->prescaler_mode = 0;
442*4882a593Smuzhiyun 	state->lna_gain = LNA_MAX_GAIN;
443*4882a593Smuzhiyun 	state->rfvga_bias_ctrl = 1;
444*4882a593Smuzhiyun 	state->Fwindow_enabled = 1;
445*4882a593Smuzhiyun 
446*4882a593Smuzhiyun 	cx24113_set_Fref(state, 0);
447*4882a593Smuzhiyun 	cx24113_enable(state, 0x3d);
448*4882a593Smuzhiyun 	cx24113_set_parameters(state);
449*4882a593Smuzhiyun 
450*4882a593Smuzhiyun 	cx24113_set_gain_settings(state, -30);
451*4882a593Smuzhiyun 
452*4882a593Smuzhiyun 	cx24113_set_bandwidth(state, 18025);
453*4882a593Smuzhiyun 	cx24113_set_clk_inversion(state, 1);
454*4882a593Smuzhiyun 
455*4882a593Smuzhiyun 	if (state->config->xtal_khz >= 40000)
456*4882a593Smuzhiyun 		ret = cx24113_writereg(state, 0x02,
457*4882a593Smuzhiyun 			(cx24113_readreg(state, 0x02) & 0xfb) | (1 << 2));
458*4882a593Smuzhiyun 	else
459*4882a593Smuzhiyun 		ret = cx24113_writereg(state, 0x02,
460*4882a593Smuzhiyun 			(cx24113_readreg(state, 0x02) & 0xfb) | (0 << 2));
461*4882a593Smuzhiyun 
462*4882a593Smuzhiyun 	return ret;
463*4882a593Smuzhiyun }
464*4882a593Smuzhiyun 
cx24113_set_params(struct dvb_frontend * fe)465*4882a593Smuzhiyun static int cx24113_set_params(struct dvb_frontend *fe)
466*4882a593Smuzhiyun {
467*4882a593Smuzhiyun 	struct dtv_frontend_properties *c = &fe->dtv_property_cache;
468*4882a593Smuzhiyun 	struct cx24113_state *state = fe->tuner_priv;
469*4882a593Smuzhiyun 	/* for a ROLL-OFF factor of 0.35, 0.2: 600, 0.25: 625 */
470*4882a593Smuzhiyun 	u32 roll_off = 675;
471*4882a593Smuzhiyun 	u32 bw;
472*4882a593Smuzhiyun 
473*4882a593Smuzhiyun 	bw  = ((c->symbol_rate/100) * roll_off) / 1000;
474*4882a593Smuzhiyun 	bw += (10000000/100) + 5;
475*4882a593Smuzhiyun 	bw /= 10;
476*4882a593Smuzhiyun 	bw += 1000;
477*4882a593Smuzhiyun 	cx24113_set_bandwidth(state, bw);
478*4882a593Smuzhiyun 
479*4882a593Smuzhiyun 	cx24113_set_frequency(state, c->frequency);
480*4882a593Smuzhiyun 	msleep(5);
481*4882a593Smuzhiyun 	return cx24113_get_status(fe, &bw);
482*4882a593Smuzhiyun }
483*4882a593Smuzhiyun 
484*4882a593Smuzhiyun static s8 cx24113_agc_table[2][10] = {
485*4882a593Smuzhiyun 	{-54, -41, -35, -30, -25, -21, -16, -10,  -6,  -2},
486*4882a593Smuzhiyun 	{-39, -35, -30, -25, -19, -15, -11,  -5,   1,   9},
487*4882a593Smuzhiyun };
488*4882a593Smuzhiyun 
cx24113_agc_callback(struct dvb_frontend * fe)489*4882a593Smuzhiyun void cx24113_agc_callback(struct dvb_frontend *fe)
490*4882a593Smuzhiyun {
491*4882a593Smuzhiyun 	struct cx24113_state *state = fe->tuner_priv;
492*4882a593Smuzhiyun 	s16 s, i;
493*4882a593Smuzhiyun 	if (!fe->ops.read_signal_strength)
494*4882a593Smuzhiyun 		return;
495*4882a593Smuzhiyun 
496*4882a593Smuzhiyun 	do {
497*4882a593Smuzhiyun 		/* this only works with the current CX24123 implementation */
498*4882a593Smuzhiyun 		fe->ops.read_signal_strength(fe, (u16 *) &s);
499*4882a593Smuzhiyun 		s >>= 8;
500*4882a593Smuzhiyun 		dprintk("signal strength: %d\n", s);
501*4882a593Smuzhiyun 		for (i = 0; i < sizeof(cx24113_agc_table[0]); i++)
502*4882a593Smuzhiyun 			if (cx24113_agc_table[state->gain_level][i] > s)
503*4882a593Smuzhiyun 				break;
504*4882a593Smuzhiyun 		s = -25 - i*5;
505*4882a593Smuzhiyun 	} while (cx24113_set_gain_settings(state, s));
506*4882a593Smuzhiyun }
507*4882a593Smuzhiyun EXPORT_SYMBOL(cx24113_agc_callback);
508*4882a593Smuzhiyun 
cx24113_get_frequency(struct dvb_frontend * fe,u32 * frequency)509*4882a593Smuzhiyun static int cx24113_get_frequency(struct dvb_frontend *fe, u32 *frequency)
510*4882a593Smuzhiyun {
511*4882a593Smuzhiyun 	struct cx24113_state *state = fe->tuner_priv;
512*4882a593Smuzhiyun 	*frequency = state->frequency;
513*4882a593Smuzhiyun 	return 0;
514*4882a593Smuzhiyun }
515*4882a593Smuzhiyun 
cx24113_release(struct dvb_frontend * fe)516*4882a593Smuzhiyun static void cx24113_release(struct dvb_frontend *fe)
517*4882a593Smuzhiyun {
518*4882a593Smuzhiyun 	struct cx24113_state *state = fe->tuner_priv;
519*4882a593Smuzhiyun 	dprintk("\n");
520*4882a593Smuzhiyun 	fe->tuner_priv = NULL;
521*4882a593Smuzhiyun 	kfree(state);
522*4882a593Smuzhiyun }
523*4882a593Smuzhiyun 
524*4882a593Smuzhiyun static const struct dvb_tuner_ops cx24113_tuner_ops = {
525*4882a593Smuzhiyun 	.info = {
526*4882a593Smuzhiyun 		.name              = "Conexant CX24113",
527*4882a593Smuzhiyun 		.frequency_min_hz  =  950 * MHz,
528*4882a593Smuzhiyun 		.frequency_max_hz  = 2150 * MHz,
529*4882a593Smuzhiyun 		.frequency_step_hz =  125 * kHz,
530*4882a593Smuzhiyun 	},
531*4882a593Smuzhiyun 
532*4882a593Smuzhiyun 	.release       = cx24113_release,
533*4882a593Smuzhiyun 
534*4882a593Smuzhiyun 	.init          = cx24113_init,
535*4882a593Smuzhiyun 
536*4882a593Smuzhiyun 	.set_params    = cx24113_set_params,
537*4882a593Smuzhiyun 	.get_frequency = cx24113_get_frequency,
538*4882a593Smuzhiyun 	.get_status    = cx24113_get_status,
539*4882a593Smuzhiyun };
540*4882a593Smuzhiyun 
cx24113_attach(struct dvb_frontend * fe,const struct cx24113_config * config,struct i2c_adapter * i2c)541*4882a593Smuzhiyun struct dvb_frontend *cx24113_attach(struct dvb_frontend *fe,
542*4882a593Smuzhiyun 		const struct cx24113_config *config, struct i2c_adapter *i2c)
543*4882a593Smuzhiyun {
544*4882a593Smuzhiyun 	/* allocate memory for the internal state */
545*4882a593Smuzhiyun 	struct cx24113_state *state = kzalloc(sizeof(*state), GFP_KERNEL);
546*4882a593Smuzhiyun 	int rc;
547*4882a593Smuzhiyun 
548*4882a593Smuzhiyun 	if (!state)
549*4882a593Smuzhiyun 		return NULL;
550*4882a593Smuzhiyun 
551*4882a593Smuzhiyun 	/* setup the state */
552*4882a593Smuzhiyun 	state->config = config;
553*4882a593Smuzhiyun 	state->i2c = i2c;
554*4882a593Smuzhiyun 
555*4882a593Smuzhiyun 	cx_info("trying to detect myself\n");
556*4882a593Smuzhiyun 
557*4882a593Smuzhiyun 	/* making a dummy read, because of some expected troubles
558*4882a593Smuzhiyun 	 * after power on */
559*4882a593Smuzhiyun 	cx24113_readreg(state, 0x00);
560*4882a593Smuzhiyun 
561*4882a593Smuzhiyun 	rc = cx24113_readreg(state, 0x00);
562*4882a593Smuzhiyun 	if (rc < 0) {
563*4882a593Smuzhiyun 		cx_info("CX24113 not found.\n");
564*4882a593Smuzhiyun 		goto error;
565*4882a593Smuzhiyun 	}
566*4882a593Smuzhiyun 	state->rev = rc;
567*4882a593Smuzhiyun 
568*4882a593Smuzhiyun 	switch (rc) {
569*4882a593Smuzhiyun 	case 0x43:
570*4882a593Smuzhiyun 		cx_info("detected CX24113 variant\n");
571*4882a593Smuzhiyun 		break;
572*4882a593Smuzhiyun 	case REV_CX24113:
573*4882a593Smuzhiyun 		cx_info("successfully detected\n");
574*4882a593Smuzhiyun 		break;
575*4882a593Smuzhiyun 	default:
576*4882a593Smuzhiyun 		cx_err("unsupported device id: %x\n", state->rev);
577*4882a593Smuzhiyun 		goto error;
578*4882a593Smuzhiyun 	}
579*4882a593Smuzhiyun 	state->ver = cx24113_readreg(state, 0x01);
580*4882a593Smuzhiyun 	cx_info("version: %x\n", state->ver);
581*4882a593Smuzhiyun 
582*4882a593Smuzhiyun 	/* create dvb_frontend */
583*4882a593Smuzhiyun 	memcpy(&fe->ops.tuner_ops, &cx24113_tuner_ops,
584*4882a593Smuzhiyun 			sizeof(struct dvb_tuner_ops));
585*4882a593Smuzhiyun 	fe->tuner_priv = state;
586*4882a593Smuzhiyun 	return fe;
587*4882a593Smuzhiyun 
588*4882a593Smuzhiyun error:
589*4882a593Smuzhiyun 	kfree(state);
590*4882a593Smuzhiyun 
591*4882a593Smuzhiyun 	return NULL;
592*4882a593Smuzhiyun }
593*4882a593Smuzhiyun EXPORT_SYMBOL(cx24113_attach);
594*4882a593Smuzhiyun 
595*4882a593Smuzhiyun module_param(debug, int, 0644);
596*4882a593Smuzhiyun MODULE_PARM_DESC(debug, "Activates frontend debugging (default:0)");
597*4882a593Smuzhiyun 
598*4882a593Smuzhiyun MODULE_AUTHOR("Patrick Boettcher <pb@linuxtv.org>");
599*4882a593Smuzhiyun MODULE_DESCRIPTION("DVB Frontend module for Conexant CX24113/CX24128hardware");
600*4882a593Smuzhiyun MODULE_LICENSE("GPL");
601*4882a593Smuzhiyun 
602