xref: /OK3568_Linux_fs/kernel/drivers/media/tuners/qm1d1b0004.c (revision 4882a59341e53eb6f0b4789bf948001014eff981)
1*4882a593Smuzhiyun // SPDX-License-Identifier: GPL-2.0
2*4882a593Smuzhiyun /*
3*4882a593Smuzhiyun  * Sharp QM1D1B0004 satellite tuner
4*4882a593Smuzhiyun  *
5*4882a593Smuzhiyun  * Copyright (C) 2014 Akihiro Tsukada <tskd08@gmail.com>
6*4882a593Smuzhiyun  *
7*4882a593Smuzhiyun  * based on (former) drivers/media/pci/pt1/va1j5jf8007s.c.
8*4882a593Smuzhiyun  */
9*4882a593Smuzhiyun 
10*4882a593Smuzhiyun /*
11*4882a593Smuzhiyun  * Note:
12*4882a593Smuzhiyun  * Since the data-sheet of this tuner chip is not available,
13*4882a593Smuzhiyun  * this driver lacks some tuner_ops and config options.
14*4882a593Smuzhiyun  * In addition, the implementation might be dependent on the specific use
15*4882a593Smuzhiyun  * in the FE module: VA1J5JF8007S and/or in the product: Earthsoft PT1/PT2.
16*4882a593Smuzhiyun  */
17*4882a593Smuzhiyun 
18*4882a593Smuzhiyun #include <linux/kernel.h>
19*4882a593Smuzhiyun #include <linux/module.h>
20*4882a593Smuzhiyun #include <media/dvb_frontend.h>
21*4882a593Smuzhiyun #include "qm1d1b0004.h"
22*4882a593Smuzhiyun 
23*4882a593Smuzhiyun /*
24*4882a593Smuzhiyun  * Tuner I/F (copied from the former va1j5jf8007s.c)
25*4882a593Smuzhiyun  * b[0] I2C addr
26*4882a593Smuzhiyun  * b[1] "0":1, BG:2, divider_quotient[7:3]:5
27*4882a593Smuzhiyun  * b[2] divider_quotient[2:0]:3, divider_remainder:5
28*4882a593Smuzhiyun  * b[3] "111":3, LPF[3:2]:2, TM:1, "0":1, REF:1
29*4882a593Smuzhiyun  * b[4] BANDX, PSC:1, LPF[1:0]:2, DIV:1, "0":1
30*4882a593Smuzhiyun  *
31*4882a593Smuzhiyun  * PLL frequency step :=
32*4882a593Smuzhiyun  *    REF == 0 -> PLL XTL frequency(4MHz) / 8
33*4882a593Smuzhiyun  *    REF == 1 -> PLL XTL frequency(4MHz) / 4
34*4882a593Smuzhiyun  *
35*4882a593Smuzhiyun  * PreScaler :=
36*4882a593Smuzhiyun  *    PSC == 0 -> x32
37*4882a593Smuzhiyun  *    PSC == 1 -> x16
38*4882a593Smuzhiyun  *
39*4882a593Smuzhiyun  * divider_quotient := (frequency / PLL frequency step) / PreScaler
40*4882a593Smuzhiyun  * divider_remainder := (frequency / PLL frequency step) % PreScaler
41*4882a593Smuzhiyun  *
42*4882a593Smuzhiyun  * LPF := LPF Frequency / 1000 / 2 - 2
43*4882a593Smuzhiyun  * LPF Frequency @ baudrate=28.86Mbps = 30000
44*4882a593Smuzhiyun  *
45*4882a593Smuzhiyun  * band (1..9)
46*4882a593Smuzhiyun  *   band 1 (freq <  986000) -> DIV:1, BANDX:5, PSC:1
47*4882a593Smuzhiyun  *   band 2 (freq < 1072000) -> DIV:1, BANDX:6, PSC:1
48*4882a593Smuzhiyun  *   band 3 (freq < 1154000) -> DIV:1, BANDX:7, PSC:0
49*4882a593Smuzhiyun  *   band 4 (freq < 1291000) -> DIV:0, BANDX:1, PSC:0
50*4882a593Smuzhiyun  *   band 5 (freq < 1447000) -> DIV:0, BANDX:2, PSC:0
51*4882a593Smuzhiyun  *   band 6 (freq < 1615000) -> DIV:0, BANDX:3, PSC:0
52*4882a593Smuzhiyun  *   band 7 (freq < 1791000) -> DIV:0, BANDX:4, PSC:0
53*4882a593Smuzhiyun  *   band 8 (freq < 1972000) -> DIV:0, BANDX:5, PSC:0
54*4882a593Smuzhiyun  *   band 9 (freq < 2150000) -> DIV:0, BANDX:6, PSC:0
55*4882a593Smuzhiyun  */
56*4882a593Smuzhiyun 
57*4882a593Smuzhiyun #define QM1D1B0004_PSC_MASK (1 << 4)
58*4882a593Smuzhiyun 
59*4882a593Smuzhiyun #define QM1D1B0004_XTL_FREQ 4000
60*4882a593Smuzhiyun #define QM1D1B0004_LPF_FALLBACK 30000
61*4882a593Smuzhiyun 
62*4882a593Smuzhiyun #if 0 /* Currently unused */
63*4882a593Smuzhiyun static const struct qm1d1b0004_config default_cfg = {
64*4882a593Smuzhiyun 	.lpf_freq = QM1D1B0004_CFG_LPF_DFLT,
65*4882a593Smuzhiyun 	.half_step = false,
66*4882a593Smuzhiyun };
67*4882a593Smuzhiyun #endif
68*4882a593Smuzhiyun 
69*4882a593Smuzhiyun struct qm1d1b0004_state {
70*4882a593Smuzhiyun 	struct qm1d1b0004_config cfg;
71*4882a593Smuzhiyun 	struct i2c_client *i2c;
72*4882a593Smuzhiyun };
73*4882a593Smuzhiyun 
74*4882a593Smuzhiyun 
75*4882a593Smuzhiyun struct qm1d1b0004_cb_map {
76*4882a593Smuzhiyun 	u32 frequency;
77*4882a593Smuzhiyun 	u8 cb;
78*4882a593Smuzhiyun };
79*4882a593Smuzhiyun 
80*4882a593Smuzhiyun static const struct qm1d1b0004_cb_map cb_maps[] = {
81*4882a593Smuzhiyun 	{  986000, 0xb2 },
82*4882a593Smuzhiyun 	{ 1072000, 0xd2 },
83*4882a593Smuzhiyun 	{ 1154000, 0xe2 },
84*4882a593Smuzhiyun 	{ 1291000, 0x20 },
85*4882a593Smuzhiyun 	{ 1447000, 0x40 },
86*4882a593Smuzhiyun 	{ 1615000, 0x60 },
87*4882a593Smuzhiyun 	{ 1791000, 0x80 },
88*4882a593Smuzhiyun 	{ 1972000, 0xa0 },
89*4882a593Smuzhiyun };
90*4882a593Smuzhiyun 
lookup_cb(u32 frequency)91*4882a593Smuzhiyun static u8 lookup_cb(u32 frequency)
92*4882a593Smuzhiyun {
93*4882a593Smuzhiyun 	int i;
94*4882a593Smuzhiyun 	const struct qm1d1b0004_cb_map *map;
95*4882a593Smuzhiyun 
96*4882a593Smuzhiyun 	for (i = 0; i < ARRAY_SIZE(cb_maps); i++) {
97*4882a593Smuzhiyun 		map = &cb_maps[i];
98*4882a593Smuzhiyun 		if (frequency < map->frequency)
99*4882a593Smuzhiyun 			return map->cb;
100*4882a593Smuzhiyun 	}
101*4882a593Smuzhiyun 	return 0xc0;
102*4882a593Smuzhiyun }
103*4882a593Smuzhiyun 
qm1d1b0004_set_params(struct dvb_frontend * fe)104*4882a593Smuzhiyun static int qm1d1b0004_set_params(struct dvb_frontend *fe)
105*4882a593Smuzhiyun {
106*4882a593Smuzhiyun 	struct qm1d1b0004_state *state;
107*4882a593Smuzhiyun 	u32 frequency, pll, lpf_freq;
108*4882a593Smuzhiyun 	u16 word;
109*4882a593Smuzhiyun 	u8 buf[4], cb, lpf;
110*4882a593Smuzhiyun 	int ret;
111*4882a593Smuzhiyun 
112*4882a593Smuzhiyun 	state = fe->tuner_priv;
113*4882a593Smuzhiyun 	frequency = fe->dtv_property_cache.frequency;
114*4882a593Smuzhiyun 
115*4882a593Smuzhiyun 	pll = QM1D1B0004_XTL_FREQ / 4;
116*4882a593Smuzhiyun 	if (state->cfg.half_step)
117*4882a593Smuzhiyun 		pll /= 2;
118*4882a593Smuzhiyun 	word = DIV_ROUND_CLOSEST(frequency, pll);
119*4882a593Smuzhiyun 	cb = lookup_cb(frequency);
120*4882a593Smuzhiyun 	if (cb & QM1D1B0004_PSC_MASK)
121*4882a593Smuzhiyun 		word = (word << 1 & ~0x1f) | (word & 0x0f);
122*4882a593Smuzhiyun 
123*4882a593Smuzhiyun 	/* step.1: set frequency with BG:2, TM:0(4MHZ), LPF:4MHz */
124*4882a593Smuzhiyun 	buf[0] = 0x40 | word >> 8;
125*4882a593Smuzhiyun 	buf[1] = word;
126*4882a593Smuzhiyun 	/* inconsisnten with the above I/F doc. maybe the doc is wrong */
127*4882a593Smuzhiyun 	buf[2] = 0xe0 | state->cfg.half_step;
128*4882a593Smuzhiyun 	buf[3] = cb;
129*4882a593Smuzhiyun 	ret = i2c_master_send(state->i2c, buf, 4);
130*4882a593Smuzhiyun 	if (ret < 0)
131*4882a593Smuzhiyun 		return ret;
132*4882a593Smuzhiyun 
133*4882a593Smuzhiyun 	/* step.2: set TM:1 */
134*4882a593Smuzhiyun 	buf[0] = 0xe4 | state->cfg.half_step;
135*4882a593Smuzhiyun 	ret = i2c_master_send(state->i2c, buf, 1);
136*4882a593Smuzhiyun 	if (ret < 0)
137*4882a593Smuzhiyun 		return ret;
138*4882a593Smuzhiyun 	msleep(20);
139*4882a593Smuzhiyun 
140*4882a593Smuzhiyun 	/* step.3: set LPF */
141*4882a593Smuzhiyun 	lpf_freq = state->cfg.lpf_freq;
142*4882a593Smuzhiyun 	if (lpf_freq == QM1D1B0004_CFG_LPF_DFLT)
143*4882a593Smuzhiyun 		lpf_freq = fe->dtv_property_cache.symbol_rate / 1000;
144*4882a593Smuzhiyun 	if (lpf_freq == 0)
145*4882a593Smuzhiyun 		lpf_freq = QM1D1B0004_LPF_FALLBACK;
146*4882a593Smuzhiyun 	lpf = DIV_ROUND_UP(lpf_freq, 2000) - 2;
147*4882a593Smuzhiyun 	buf[0] = 0xe4 | ((lpf & 0x0c) << 1) | state->cfg.half_step;
148*4882a593Smuzhiyun 	buf[1] = cb | ((lpf & 0x03) << 2);
149*4882a593Smuzhiyun 	ret = i2c_master_send(state->i2c, buf, 2);
150*4882a593Smuzhiyun 	if (ret < 0)
151*4882a593Smuzhiyun 		return ret;
152*4882a593Smuzhiyun 
153*4882a593Smuzhiyun 	/* step.4: read PLL lock? */
154*4882a593Smuzhiyun 	buf[0] = 0;
155*4882a593Smuzhiyun 	ret = i2c_master_recv(state->i2c, buf, 1);
156*4882a593Smuzhiyun 	if (ret < 0)
157*4882a593Smuzhiyun 		return ret;
158*4882a593Smuzhiyun 	return 0;
159*4882a593Smuzhiyun }
160*4882a593Smuzhiyun 
161*4882a593Smuzhiyun 
qm1d1b0004_set_config(struct dvb_frontend * fe,void * priv_cfg)162*4882a593Smuzhiyun static int qm1d1b0004_set_config(struct dvb_frontend *fe, void *priv_cfg)
163*4882a593Smuzhiyun {
164*4882a593Smuzhiyun 	struct qm1d1b0004_state *state;
165*4882a593Smuzhiyun 
166*4882a593Smuzhiyun 	state = fe->tuner_priv;
167*4882a593Smuzhiyun 	memcpy(&state->cfg, priv_cfg, sizeof(state->cfg));
168*4882a593Smuzhiyun 	return 0;
169*4882a593Smuzhiyun }
170*4882a593Smuzhiyun 
171*4882a593Smuzhiyun 
qm1d1b0004_init(struct dvb_frontend * fe)172*4882a593Smuzhiyun static int qm1d1b0004_init(struct dvb_frontend *fe)
173*4882a593Smuzhiyun {
174*4882a593Smuzhiyun 	struct qm1d1b0004_state *state;
175*4882a593Smuzhiyun 	u8 buf[2] = {0xf8, 0x04};
176*4882a593Smuzhiyun 
177*4882a593Smuzhiyun 	state = fe->tuner_priv;
178*4882a593Smuzhiyun 	if (state->cfg.half_step)
179*4882a593Smuzhiyun 		buf[0] |= 0x01;
180*4882a593Smuzhiyun 
181*4882a593Smuzhiyun 	return i2c_master_send(state->i2c, buf, 2);
182*4882a593Smuzhiyun }
183*4882a593Smuzhiyun 
184*4882a593Smuzhiyun 
185*4882a593Smuzhiyun static const struct dvb_tuner_ops qm1d1b0004_ops = {
186*4882a593Smuzhiyun 	.info = {
187*4882a593Smuzhiyun 		.name = "Sharp qm1d1b0004",
188*4882a593Smuzhiyun 
189*4882a593Smuzhiyun 		.frequency_min_hz =  950 * MHz,
190*4882a593Smuzhiyun 		.frequency_max_hz = 2150 * MHz,
191*4882a593Smuzhiyun 	},
192*4882a593Smuzhiyun 
193*4882a593Smuzhiyun 	.init = qm1d1b0004_init,
194*4882a593Smuzhiyun 
195*4882a593Smuzhiyun 	.set_params = qm1d1b0004_set_params,
196*4882a593Smuzhiyun 	.set_config = qm1d1b0004_set_config,
197*4882a593Smuzhiyun };
198*4882a593Smuzhiyun 
199*4882a593Smuzhiyun static int
qm1d1b0004_probe(struct i2c_client * client,const struct i2c_device_id * id)200*4882a593Smuzhiyun qm1d1b0004_probe(struct i2c_client *client, const struct i2c_device_id *id)
201*4882a593Smuzhiyun {
202*4882a593Smuzhiyun 	struct dvb_frontend *fe;
203*4882a593Smuzhiyun 	struct qm1d1b0004_config *cfg;
204*4882a593Smuzhiyun 	struct qm1d1b0004_state *state;
205*4882a593Smuzhiyun 	int ret;
206*4882a593Smuzhiyun 
207*4882a593Smuzhiyun 	cfg = client->dev.platform_data;
208*4882a593Smuzhiyun 	fe = cfg->fe;
209*4882a593Smuzhiyun 	i2c_set_clientdata(client, fe);
210*4882a593Smuzhiyun 
211*4882a593Smuzhiyun 	fe->tuner_priv = kzalloc(sizeof(struct qm1d1b0004_state), GFP_KERNEL);
212*4882a593Smuzhiyun 	if (!fe->tuner_priv) {
213*4882a593Smuzhiyun 		ret = -ENOMEM;
214*4882a593Smuzhiyun 		goto err_mem;
215*4882a593Smuzhiyun 	}
216*4882a593Smuzhiyun 
217*4882a593Smuzhiyun 	memcpy(&fe->ops.tuner_ops, &qm1d1b0004_ops, sizeof(fe->ops.tuner_ops));
218*4882a593Smuzhiyun 
219*4882a593Smuzhiyun 	state = fe->tuner_priv;
220*4882a593Smuzhiyun 	state->i2c = client;
221*4882a593Smuzhiyun 	ret = qm1d1b0004_set_config(fe, cfg);
222*4882a593Smuzhiyun 	if (ret != 0)
223*4882a593Smuzhiyun 		goto err_priv;
224*4882a593Smuzhiyun 
225*4882a593Smuzhiyun 	dev_info(&client->dev, "Sharp QM1D1B0004 attached.\n");
226*4882a593Smuzhiyun 	return 0;
227*4882a593Smuzhiyun 
228*4882a593Smuzhiyun err_priv:
229*4882a593Smuzhiyun 	kfree(fe->tuner_priv);
230*4882a593Smuzhiyun err_mem:
231*4882a593Smuzhiyun 	fe->tuner_priv = NULL;
232*4882a593Smuzhiyun 	return ret;
233*4882a593Smuzhiyun }
234*4882a593Smuzhiyun 
qm1d1b0004_remove(struct i2c_client * client)235*4882a593Smuzhiyun static int qm1d1b0004_remove(struct i2c_client *client)
236*4882a593Smuzhiyun {
237*4882a593Smuzhiyun 	struct dvb_frontend *fe;
238*4882a593Smuzhiyun 
239*4882a593Smuzhiyun 	fe = i2c_get_clientdata(client);
240*4882a593Smuzhiyun 	kfree(fe->tuner_priv);
241*4882a593Smuzhiyun 	fe->tuner_priv = NULL;
242*4882a593Smuzhiyun 	return 0;
243*4882a593Smuzhiyun }
244*4882a593Smuzhiyun 
245*4882a593Smuzhiyun 
246*4882a593Smuzhiyun static const struct i2c_device_id qm1d1b0004_id[] = {
247*4882a593Smuzhiyun 	{"qm1d1b0004", 0},
248*4882a593Smuzhiyun 	{}
249*4882a593Smuzhiyun };
250*4882a593Smuzhiyun 
251*4882a593Smuzhiyun MODULE_DEVICE_TABLE(i2c, qm1d1b0004_id);
252*4882a593Smuzhiyun 
253*4882a593Smuzhiyun static struct i2c_driver qm1d1b0004_driver = {
254*4882a593Smuzhiyun 	.driver = {
255*4882a593Smuzhiyun 		.name = "qm1d1b0004",
256*4882a593Smuzhiyun 	},
257*4882a593Smuzhiyun 	.probe    = qm1d1b0004_probe,
258*4882a593Smuzhiyun 	.remove   = qm1d1b0004_remove,
259*4882a593Smuzhiyun 	.id_table = qm1d1b0004_id,
260*4882a593Smuzhiyun };
261*4882a593Smuzhiyun 
262*4882a593Smuzhiyun module_i2c_driver(qm1d1b0004_driver);
263*4882a593Smuzhiyun 
264*4882a593Smuzhiyun MODULE_DESCRIPTION("Sharp QM1D1B0004");
265*4882a593Smuzhiyun MODULE_AUTHOR("Akihiro Tsukada");
266*4882a593Smuzhiyun MODULE_LICENSE("GPL");
267