1 | // SPDX-License-Identifier: GPL-2.0-or-later |
2 | /* |
3 | * Driver for Freescale MC44S803 Low Power CMOS Broadband Tuner |
4 | * |
5 | * Copyright (c) 2009 Jochen Friedrich <jochen@scram.de> |
6 | */ |
7 | |
8 | #include <linux/module.h> |
9 | #include <linux/delay.h> |
10 | #include <linux/dvb/frontend.h> |
11 | #include <linux/i2c.h> |
12 | #include <linux/slab.h> |
13 | |
14 | #include <media/dvb_frontend.h> |
15 | |
16 | #include "mc44s803.h" |
17 | #include "mc44s803_priv.h" |
18 | |
19 | #define mc_printk(level, format, arg...) \ |
20 | printk(level "mc44s803: " format , ## arg) |
21 | |
22 | /* Writes a single register */ |
23 | static int mc44s803_writereg(struct mc44s803_priv *priv, u32 val) |
24 | { |
25 | u8 buf[3]; |
26 | struct i2c_msg msg = { |
27 | .addr = priv->cfg->i2c_address, .flags = 0, .buf = buf, .len = 3 |
28 | }; |
29 | |
30 | buf[0] = (val & 0xff0000) >> 16; |
31 | buf[1] = (val & 0xff00) >> 8; |
32 | buf[2] = (val & 0xff); |
33 | |
34 | if (i2c_transfer(adap: priv->i2c, msgs: &msg, num: 1) != 1) { |
35 | mc_printk(KERN_WARNING, "I2C write failed\n" ); |
36 | return -EREMOTEIO; |
37 | } |
38 | return 0; |
39 | } |
40 | |
41 | /* Reads a single register */ |
42 | static int mc44s803_readreg(struct mc44s803_priv *priv, u8 reg, u32 *val) |
43 | { |
44 | u32 wval; |
45 | u8 buf[3]; |
46 | int ret; |
47 | struct i2c_msg msg[] = { |
48 | { .addr = priv->cfg->i2c_address, .flags = I2C_M_RD, |
49 | .buf = buf, .len = 3 }, |
50 | }; |
51 | |
52 | wval = MC44S803_REG_SM(MC44S803_REG_DATAREG, MC44S803_ADDR) | |
53 | MC44S803_REG_SM(reg, MC44S803_D); |
54 | |
55 | ret = mc44s803_writereg(priv, val: wval); |
56 | if (ret) |
57 | return ret; |
58 | |
59 | if (i2c_transfer(adap: priv->i2c, msgs: msg, num: 1) != 1) { |
60 | mc_printk(KERN_WARNING, "I2C read failed\n" ); |
61 | return -EREMOTEIO; |
62 | } |
63 | |
64 | *val = (buf[0] << 16) | (buf[1] << 8) | buf[2]; |
65 | |
66 | return 0; |
67 | } |
68 | |
69 | static void mc44s803_release(struct dvb_frontend *fe) |
70 | { |
71 | struct mc44s803_priv *priv = fe->tuner_priv; |
72 | |
73 | fe->tuner_priv = NULL; |
74 | kfree(objp: priv); |
75 | } |
76 | |
77 | static int mc44s803_init(struct dvb_frontend *fe) |
78 | { |
79 | struct mc44s803_priv *priv = fe->tuner_priv; |
80 | u32 val; |
81 | int err; |
82 | |
83 | if (fe->ops.i2c_gate_ctrl) |
84 | fe->ops.i2c_gate_ctrl(fe, 1); |
85 | |
86 | /* Reset chip */ |
87 | val = MC44S803_REG_SM(MC44S803_REG_RESET, MC44S803_ADDR) | |
88 | MC44S803_REG_SM(1, MC44S803_RS); |
89 | |
90 | err = mc44s803_writereg(priv, val); |
91 | if (err) |
92 | goto exit; |
93 | |
94 | val = MC44S803_REG_SM(MC44S803_REG_RESET, MC44S803_ADDR); |
95 | |
96 | err = mc44s803_writereg(priv, val); |
97 | if (err) |
98 | goto exit; |
99 | |
100 | /* Power Up and Start Osc */ |
101 | |
102 | val = MC44S803_REG_SM(MC44S803_REG_REFOSC, MC44S803_ADDR) | |
103 | MC44S803_REG_SM(0xC0, MC44S803_REFOSC) | |
104 | MC44S803_REG_SM(1, MC44S803_OSCSEL); |
105 | |
106 | err = mc44s803_writereg(priv, val); |
107 | if (err) |
108 | goto exit; |
109 | |
110 | val = MC44S803_REG_SM(MC44S803_REG_POWER, MC44S803_ADDR) | |
111 | MC44S803_REG_SM(0x200, MC44S803_POWER); |
112 | |
113 | err = mc44s803_writereg(priv, val); |
114 | if (err) |
115 | goto exit; |
116 | |
117 | msleep(msecs: 10); |
118 | |
119 | val = MC44S803_REG_SM(MC44S803_REG_REFOSC, MC44S803_ADDR) | |
120 | MC44S803_REG_SM(0x40, MC44S803_REFOSC) | |
121 | MC44S803_REG_SM(1, MC44S803_OSCSEL); |
122 | |
123 | err = mc44s803_writereg(priv, val); |
124 | if (err) |
125 | goto exit; |
126 | |
127 | msleep(msecs: 20); |
128 | |
129 | /* Setup Mixer */ |
130 | |
131 | val = MC44S803_REG_SM(MC44S803_REG_MIXER, MC44S803_ADDR) | |
132 | MC44S803_REG_SM(1, MC44S803_TRI_STATE) | |
133 | MC44S803_REG_SM(0x7F, MC44S803_MIXER_RES); |
134 | |
135 | err = mc44s803_writereg(priv, val); |
136 | if (err) |
137 | goto exit; |
138 | |
139 | /* Setup Cirquit Adjust */ |
140 | |
141 | val = MC44S803_REG_SM(MC44S803_REG_CIRCADJ, MC44S803_ADDR) | |
142 | MC44S803_REG_SM(1, MC44S803_G1) | |
143 | MC44S803_REG_SM(1, MC44S803_G3) | |
144 | MC44S803_REG_SM(0x3, MC44S803_CIRCADJ_RES) | |
145 | MC44S803_REG_SM(1, MC44S803_G6) | |
146 | MC44S803_REG_SM(priv->cfg->dig_out, MC44S803_S1) | |
147 | MC44S803_REG_SM(0x3, MC44S803_LP) | |
148 | MC44S803_REG_SM(1, MC44S803_CLRF) | |
149 | MC44S803_REG_SM(1, MC44S803_CLIF); |
150 | |
151 | err = mc44s803_writereg(priv, val); |
152 | if (err) |
153 | goto exit; |
154 | |
155 | val = MC44S803_REG_SM(MC44S803_REG_CIRCADJ, MC44S803_ADDR) | |
156 | MC44S803_REG_SM(1, MC44S803_G1) | |
157 | MC44S803_REG_SM(1, MC44S803_G3) | |
158 | MC44S803_REG_SM(0x3, MC44S803_CIRCADJ_RES) | |
159 | MC44S803_REG_SM(1, MC44S803_G6) | |
160 | MC44S803_REG_SM(priv->cfg->dig_out, MC44S803_S1) | |
161 | MC44S803_REG_SM(0x3, MC44S803_LP); |
162 | |
163 | err = mc44s803_writereg(priv, val); |
164 | if (err) |
165 | goto exit; |
166 | |
167 | /* Setup Digtune */ |
168 | |
169 | val = MC44S803_REG_SM(MC44S803_REG_DIGTUNE, MC44S803_ADDR) | |
170 | MC44S803_REG_SM(3, MC44S803_XOD); |
171 | |
172 | err = mc44s803_writereg(priv, val); |
173 | if (err) |
174 | goto exit; |
175 | |
176 | /* Setup AGC */ |
177 | |
178 | val = MC44S803_REG_SM(MC44S803_REG_LNAAGC, MC44S803_ADDR) | |
179 | MC44S803_REG_SM(1, MC44S803_AT1) | |
180 | MC44S803_REG_SM(1, MC44S803_AT2) | |
181 | MC44S803_REG_SM(1, MC44S803_AGC_AN_DIG) | |
182 | MC44S803_REG_SM(1, MC44S803_AGC_READ_EN) | |
183 | MC44S803_REG_SM(1, MC44S803_LNA0); |
184 | |
185 | err = mc44s803_writereg(priv, val); |
186 | if (err) |
187 | goto exit; |
188 | |
189 | if (fe->ops.i2c_gate_ctrl) |
190 | fe->ops.i2c_gate_ctrl(fe, 0); |
191 | return 0; |
192 | |
193 | exit: |
194 | if (fe->ops.i2c_gate_ctrl) |
195 | fe->ops.i2c_gate_ctrl(fe, 0); |
196 | |
197 | mc_printk(KERN_WARNING, "I/O Error\n" ); |
198 | return err; |
199 | } |
200 | |
201 | static int mc44s803_set_params(struct dvb_frontend *fe) |
202 | { |
203 | struct mc44s803_priv *priv = fe->tuner_priv; |
204 | struct dtv_frontend_properties *c = &fe->dtv_property_cache; |
205 | u32 r1, r2, n1, n2, lo1, lo2, freq, val; |
206 | int err; |
207 | |
208 | priv->frequency = c->frequency; |
209 | |
210 | r1 = MC44S803_OSC / 1000000; |
211 | r2 = MC44S803_OSC / 100000; |
212 | |
213 | n1 = (c->frequency + MC44S803_IF1 + 500000) / 1000000; |
214 | freq = MC44S803_OSC / r1 * n1; |
215 | lo1 = ((60 * n1) + (r1 / 2)) / r1; |
216 | freq = freq - c->frequency; |
217 | |
218 | n2 = (freq - MC44S803_IF2 + 50000) / 100000; |
219 | lo2 = ((60 * n2) + (r2 / 2)) / r2; |
220 | |
221 | if (fe->ops.i2c_gate_ctrl) |
222 | fe->ops.i2c_gate_ctrl(fe, 1); |
223 | |
224 | val = MC44S803_REG_SM(MC44S803_REG_REFDIV, MC44S803_ADDR) | |
225 | MC44S803_REG_SM(r1-1, MC44S803_R1) | |
226 | MC44S803_REG_SM(r2-1, MC44S803_R2) | |
227 | MC44S803_REG_SM(1, MC44S803_REFBUF_EN); |
228 | |
229 | err = mc44s803_writereg(priv, val); |
230 | if (err) |
231 | goto exit; |
232 | |
233 | val = MC44S803_REG_SM(MC44S803_REG_LO1, MC44S803_ADDR) | |
234 | MC44S803_REG_SM(n1-2, MC44S803_LO1); |
235 | |
236 | err = mc44s803_writereg(priv, val); |
237 | if (err) |
238 | goto exit; |
239 | |
240 | val = MC44S803_REG_SM(MC44S803_REG_LO2, MC44S803_ADDR) | |
241 | MC44S803_REG_SM(n2-2, MC44S803_LO2); |
242 | |
243 | err = mc44s803_writereg(priv, val); |
244 | if (err) |
245 | goto exit; |
246 | |
247 | val = MC44S803_REG_SM(MC44S803_REG_DIGTUNE, MC44S803_ADDR) | |
248 | MC44S803_REG_SM(1, MC44S803_DA) | |
249 | MC44S803_REG_SM(lo1, MC44S803_LO_REF) | |
250 | MC44S803_REG_SM(1, MC44S803_AT); |
251 | |
252 | err = mc44s803_writereg(priv, val); |
253 | if (err) |
254 | goto exit; |
255 | |
256 | val = MC44S803_REG_SM(MC44S803_REG_DIGTUNE, MC44S803_ADDR) | |
257 | MC44S803_REG_SM(2, MC44S803_DA) | |
258 | MC44S803_REG_SM(lo2, MC44S803_LO_REF) | |
259 | MC44S803_REG_SM(1, MC44S803_AT); |
260 | |
261 | err = mc44s803_writereg(priv, val); |
262 | if (err) |
263 | goto exit; |
264 | |
265 | if (fe->ops.i2c_gate_ctrl) |
266 | fe->ops.i2c_gate_ctrl(fe, 0); |
267 | |
268 | return 0; |
269 | |
270 | exit: |
271 | if (fe->ops.i2c_gate_ctrl) |
272 | fe->ops.i2c_gate_ctrl(fe, 0); |
273 | |
274 | mc_printk(KERN_WARNING, "I/O Error\n" ); |
275 | return err; |
276 | } |
277 | |
278 | static int mc44s803_get_frequency(struct dvb_frontend *fe, u32 *frequency) |
279 | { |
280 | struct mc44s803_priv *priv = fe->tuner_priv; |
281 | *frequency = priv->frequency; |
282 | return 0; |
283 | } |
284 | |
285 | static int mc44s803_get_if_frequency(struct dvb_frontend *fe, u32 *frequency) |
286 | { |
287 | *frequency = MC44S803_IF2; /* 36.125 MHz */ |
288 | return 0; |
289 | } |
290 | |
291 | static const struct dvb_tuner_ops mc44s803_tuner_ops = { |
292 | .info = { |
293 | .name = "Freescale MC44S803" , |
294 | .frequency_min_hz = 48 * MHz, |
295 | .frequency_max_hz = 1000 * MHz, |
296 | .frequency_step_hz = 100 * kHz, |
297 | }, |
298 | |
299 | .release = mc44s803_release, |
300 | .init = mc44s803_init, |
301 | .set_params = mc44s803_set_params, |
302 | .get_frequency = mc44s803_get_frequency, |
303 | .get_if_frequency = mc44s803_get_if_frequency, |
304 | }; |
305 | |
306 | /* This functions tries to identify a MC44S803 tuner by reading the ID |
307 | register. This is hasty. */ |
308 | struct dvb_frontend *mc44s803_attach(struct dvb_frontend *fe, |
309 | struct i2c_adapter *i2c, struct mc44s803_config *cfg) |
310 | { |
311 | struct mc44s803_priv *priv; |
312 | u32 reg; |
313 | u8 id; |
314 | int ret; |
315 | |
316 | reg = 0; |
317 | |
318 | priv = kzalloc(size: sizeof(struct mc44s803_priv), GFP_KERNEL); |
319 | if (priv == NULL) |
320 | return NULL; |
321 | |
322 | priv->cfg = cfg; |
323 | priv->i2c = i2c; |
324 | priv->fe = fe; |
325 | |
326 | if (fe->ops.i2c_gate_ctrl) |
327 | fe->ops.i2c_gate_ctrl(fe, 1); /* open i2c_gate */ |
328 | |
329 | ret = mc44s803_readreg(priv, MC44S803_REG_ID, val: ®); |
330 | if (ret) |
331 | goto error; |
332 | |
333 | id = MC44S803_REG_MS(reg, MC44S803_ID); |
334 | |
335 | if (id != 0x14) { |
336 | mc_printk(KERN_ERR, "unsupported ID (%x should be 0x14)\n" , |
337 | id); |
338 | goto error; |
339 | } |
340 | |
341 | mc_printk(KERN_INFO, "successfully identified (ID = %x)\n" , id); |
342 | memcpy(&fe->ops.tuner_ops, &mc44s803_tuner_ops, |
343 | sizeof(struct dvb_tuner_ops)); |
344 | |
345 | fe->tuner_priv = priv; |
346 | |
347 | if (fe->ops.i2c_gate_ctrl) |
348 | fe->ops.i2c_gate_ctrl(fe, 0); /* close i2c_gate */ |
349 | |
350 | return fe; |
351 | |
352 | error: |
353 | if (fe->ops.i2c_gate_ctrl) |
354 | fe->ops.i2c_gate_ctrl(fe, 0); /* close i2c_gate */ |
355 | |
356 | kfree(objp: priv); |
357 | return NULL; |
358 | } |
359 | EXPORT_SYMBOL_GPL(mc44s803_attach); |
360 | |
361 | MODULE_AUTHOR("Jochen Friedrich" ); |
362 | MODULE_DESCRIPTION("Freescale MC44S803 silicon tuner driver" ); |
363 | MODULE_LICENSE("GPL" ); |
364 | |