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