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

source code of linux/drivers/media/tuners/qm1d1b0004.c