1 | // SPDX-License-Identifier: GPL-2.0-or-later |
2 | /* |
3 | * horus3a.h |
4 | * |
5 | * Sony Horus3A DVB-S/S2 tuner driver |
6 | * |
7 | * Copyright 2012 Sony Corporation |
8 | * Copyright (C) 2014 NetUP Inc. |
9 | * Copyright (C) 2014 Sergey Kozlov <serjk@netup.ru> |
10 | * Copyright (C) 2014 Abylay Ospan <aospan@netup.ru> |
11 | */ |
12 | |
13 | #include <linux/slab.h> |
14 | #include <linux/module.h> |
15 | #include <linux/dvb/frontend.h> |
16 | #include <linux/types.h> |
17 | #include "horus3a.h" |
18 | #include <media/dvb_frontend.h> |
19 | |
20 | #define MAX_WRITE_REGSIZE 5 |
21 | |
22 | enum horus3a_state { |
23 | STATE_UNKNOWN, |
24 | STATE_SLEEP, |
25 | STATE_ACTIVE |
26 | }; |
27 | |
28 | struct horus3a_priv { |
29 | u32 frequency; |
30 | u8 i2c_address; |
31 | struct i2c_adapter *i2c; |
32 | enum horus3a_state state; |
33 | void *set_tuner_data; |
34 | int (*set_tuner)(void *, int); |
35 | }; |
36 | |
37 | static void horus3a_i2c_debug(struct horus3a_priv *priv, |
38 | u8 reg, u8 write, const u8 *data, u32 len) |
39 | { |
40 | dev_dbg(&priv->i2c->dev, "horus3a: I2C %s reg 0x%02x size %d\n" , |
41 | (write == 0 ? "read" : "write" ), reg, len); |
42 | print_hex_dump_bytes("horus3a: I2C data: " , |
43 | DUMP_PREFIX_OFFSET, data, len); |
44 | } |
45 | |
46 | static int horus3a_write_regs(struct horus3a_priv *priv, |
47 | u8 reg, const u8 *data, u32 len) |
48 | { |
49 | int ret; |
50 | u8 buf[MAX_WRITE_REGSIZE + 1]; |
51 | struct i2c_msg msg[1] = { |
52 | { |
53 | .addr = priv->i2c_address, |
54 | .flags = 0, |
55 | .len = len + 1, |
56 | .buf = buf, |
57 | } |
58 | }; |
59 | |
60 | if (len + 1 > sizeof(buf)) { |
61 | dev_warn(&priv->i2c->dev,"wr reg=%04x: len=%d is too big!\n" , |
62 | reg, len + 1); |
63 | return -E2BIG; |
64 | } |
65 | |
66 | horus3a_i2c_debug(priv, reg, write: 1, data, len); |
67 | buf[0] = reg; |
68 | memcpy(&buf[1], data, len); |
69 | ret = i2c_transfer(adap: priv->i2c, msgs: msg, num: 1); |
70 | if (ret >= 0 && ret != 1) |
71 | ret = -EREMOTEIO; |
72 | if (ret < 0) { |
73 | dev_warn(&priv->i2c->dev, |
74 | "%s: i2c wr failed=%d reg=%02x len=%d\n" , |
75 | KBUILD_MODNAME, ret, reg, len); |
76 | return ret; |
77 | } |
78 | return 0; |
79 | } |
80 | |
81 | static int horus3a_write_reg(struct horus3a_priv *priv, u8 reg, u8 val) |
82 | { |
83 | u8 tmp = val; /* see gcc.gnu.org/bugzilla/show_bug.cgi?id=81715 */ |
84 | |
85 | return horus3a_write_regs(priv, reg, data: &tmp, len: 1); |
86 | } |
87 | |
88 | static int horus3a_enter_power_save(struct horus3a_priv *priv) |
89 | { |
90 | u8 data[2]; |
91 | |
92 | dev_dbg(&priv->i2c->dev, "%s()\n" , __func__); |
93 | if (priv->state == STATE_SLEEP) |
94 | return 0; |
95 | /* IQ Generator disable */ |
96 | horus3a_write_reg(priv, reg: 0x2a, val: 0x79); |
97 | /* MDIV_EN = 0 */ |
98 | horus3a_write_reg(priv, reg: 0x29, val: 0x70); |
99 | /* VCO disable preparation */ |
100 | horus3a_write_reg(priv, reg: 0x28, val: 0x3e); |
101 | /* VCO buffer disable */ |
102 | horus3a_write_reg(priv, reg: 0x2a, val: 0x19); |
103 | /* VCO calibration disable */ |
104 | horus3a_write_reg(priv, reg: 0x1c, val: 0x00); |
105 | /* Power save setting (xtal is not stopped) */ |
106 | data[0] = 0xC0; |
107 | /* LNA is Disabled */ |
108 | data[1] = 0xA7; |
109 | /* 0x11 - 0x12 */ |
110 | horus3a_write_regs(priv, reg: 0x11, data, len: sizeof(data)); |
111 | priv->state = STATE_SLEEP; |
112 | return 0; |
113 | } |
114 | |
115 | static int horus3a_leave_power_save(struct horus3a_priv *priv) |
116 | { |
117 | u8 data[2]; |
118 | |
119 | dev_dbg(&priv->i2c->dev, "%s()\n" , __func__); |
120 | if (priv->state == STATE_ACTIVE) |
121 | return 0; |
122 | /* Leave power save */ |
123 | data[0] = 0x00; |
124 | /* LNA is Disabled */ |
125 | data[1] = 0xa7; |
126 | /* 0x11 - 0x12 */ |
127 | horus3a_write_regs(priv, reg: 0x11, data, len: sizeof(data)); |
128 | /* VCO buffer enable */ |
129 | horus3a_write_reg(priv, reg: 0x2a, val: 0x79); |
130 | /* VCO calibration enable */ |
131 | horus3a_write_reg(priv, reg: 0x1c, val: 0xc0); |
132 | /* MDIV_EN = 1 */ |
133 | horus3a_write_reg(priv, reg: 0x29, val: 0x71); |
134 | usleep_range(min: 5000, max: 7000); |
135 | priv->state = STATE_ACTIVE; |
136 | return 0; |
137 | } |
138 | |
139 | static int horus3a_init(struct dvb_frontend *fe) |
140 | { |
141 | struct horus3a_priv *priv = fe->tuner_priv; |
142 | |
143 | dev_dbg(&priv->i2c->dev, "%s()\n" , __func__); |
144 | return 0; |
145 | } |
146 | |
147 | static void horus3a_release(struct dvb_frontend *fe) |
148 | { |
149 | struct horus3a_priv *priv = fe->tuner_priv; |
150 | |
151 | dev_dbg(&priv->i2c->dev, "%s()\n" , __func__); |
152 | kfree(objp: fe->tuner_priv); |
153 | fe->tuner_priv = NULL; |
154 | } |
155 | |
156 | static int horus3a_sleep(struct dvb_frontend *fe) |
157 | { |
158 | struct horus3a_priv *priv = fe->tuner_priv; |
159 | |
160 | dev_dbg(&priv->i2c->dev, "%s()\n" , __func__); |
161 | horus3a_enter_power_save(priv); |
162 | return 0; |
163 | } |
164 | |
165 | static int horus3a_set_params(struct dvb_frontend *fe) |
166 | { |
167 | struct dtv_frontend_properties *p = &fe->dtv_property_cache; |
168 | struct horus3a_priv *priv = fe->tuner_priv; |
169 | u32 frequency = p->frequency; |
170 | u32 symbol_rate = p->symbol_rate/1000; |
171 | u8 mixdiv = 0; |
172 | u8 mdiv = 0; |
173 | u32 ms = 0; |
174 | u8 f_ctl = 0; |
175 | u8 g_ctl = 0; |
176 | u8 fc_lpf = 0; |
177 | u8 data[5]; |
178 | |
179 | dev_dbg(&priv->i2c->dev, "%s(): frequency %dkHz symbol_rate %dksps\n" , |
180 | __func__, frequency, symbol_rate); |
181 | if (priv->set_tuner) |
182 | priv->set_tuner(priv->set_tuner_data, 0); |
183 | if (priv->state == STATE_SLEEP) |
184 | horus3a_leave_power_save(priv); |
185 | |
186 | /* frequency should be X MHz (X : integer) */ |
187 | frequency = DIV_ROUND_CLOSEST(frequency, 1000) * 1000; |
188 | if (frequency <= 1155000) { |
189 | mixdiv = 4; |
190 | mdiv = 1; |
191 | } else { |
192 | mixdiv = 2; |
193 | mdiv = 0; |
194 | } |
195 | /* Assumed that fREF == 1MHz (1000kHz) */ |
196 | ms = DIV_ROUND_CLOSEST((frequency * mixdiv) / 2, 1000); |
197 | if (ms > 0x7FFF) { /* 15 bit */ |
198 | dev_err(&priv->i2c->dev, "horus3a: invalid frequency %d\n" , |
199 | frequency); |
200 | return -EINVAL; |
201 | } |
202 | if (frequency < 975000) { |
203 | /* F_CTL=11100 G_CTL=001 */ |
204 | f_ctl = 0x1C; |
205 | g_ctl = 0x01; |
206 | } else if (frequency < 1050000) { |
207 | /* F_CTL=11000 G_CTL=010 */ |
208 | f_ctl = 0x18; |
209 | g_ctl = 0x02; |
210 | } else if (frequency < 1150000) { |
211 | /* F_CTL=10100 G_CTL=010 */ |
212 | f_ctl = 0x14; |
213 | g_ctl = 0x02; |
214 | } else if (frequency < 1250000) { |
215 | /* F_CTL=10000 G_CTL=011 */ |
216 | f_ctl = 0x10; |
217 | g_ctl = 0x03; |
218 | } else if (frequency < 1350000) { |
219 | /* F_CTL=01100 G_CTL=100 */ |
220 | f_ctl = 0x0C; |
221 | g_ctl = 0x04; |
222 | } else if (frequency < 1450000) { |
223 | /* F_CTL=01010 G_CTL=100 */ |
224 | f_ctl = 0x0A; |
225 | g_ctl = 0x04; |
226 | } else if (frequency < 1600000) { |
227 | /* F_CTL=00111 G_CTL=101 */ |
228 | f_ctl = 0x07; |
229 | g_ctl = 0x05; |
230 | } else if (frequency < 1800000) { |
231 | /* F_CTL=00100 G_CTL=010 */ |
232 | f_ctl = 0x04; |
233 | g_ctl = 0x02; |
234 | } else if (frequency < 2000000) { |
235 | /* F_CTL=00010 G_CTL=001 */ |
236 | f_ctl = 0x02; |
237 | g_ctl = 0x01; |
238 | } else { |
239 | /* F_CTL=00000 G_CTL=000 */ |
240 | f_ctl = 0x00; |
241 | g_ctl = 0x00; |
242 | } |
243 | /* LPF cutoff frequency setting */ |
244 | if (p->delivery_system == SYS_DVBS) { |
245 | /* |
246 | * rolloff = 0.35 |
247 | * SR <= 4.3 |
248 | * fc_lpf = 5 |
249 | * 4.3 < SR <= 10 |
250 | * fc_lpf = SR * (1 + rolloff) / 2 + SR / 2 = |
251 | * SR * 1.175 = SR * (47/40) |
252 | * 10 < SR |
253 | * fc_lpf = SR * (1 + rolloff) / 2 + 5 = |
254 | * SR * 0.675 + 5 = SR * (27/40) + 5 |
255 | * NOTE: The result should be round up. |
256 | */ |
257 | if (symbol_rate <= 4300) |
258 | fc_lpf = 5; |
259 | else if (symbol_rate <= 10000) |
260 | fc_lpf = (u8)DIV_ROUND_UP(symbol_rate * 47, 40000); |
261 | else |
262 | fc_lpf = (u8)DIV_ROUND_UP(symbol_rate * 27, 40000) + 5; |
263 | /* 5 <= fc_lpf <= 36 */ |
264 | if (fc_lpf > 36) |
265 | fc_lpf = 36; |
266 | } else if (p->delivery_system == SYS_DVBS2) { |
267 | /* |
268 | * SR <= 4.5: |
269 | * fc_lpf = 5 |
270 | * 4.5 < SR <= 10: |
271 | * fc_lpf = SR * (1 + rolloff) / 2 + SR / 2 |
272 | * 10 < SR: |
273 | * fc_lpf = SR * (1 + rolloff) / 2 + 5 |
274 | * NOTE: The result should be round up. |
275 | */ |
276 | if (symbol_rate <= 4500) |
277 | fc_lpf = 5; |
278 | else if (symbol_rate <= 10000) |
279 | fc_lpf = (u8)((symbol_rate * 11 + (10000-1)) / 10000); |
280 | else |
281 | fc_lpf = (u8)((symbol_rate * 3 + (5000-1)) / 5000 + 5); |
282 | /* 5 <= fc_lpf <= 36 is valid */ |
283 | if (fc_lpf > 36) |
284 | fc_lpf = 36; |
285 | } else { |
286 | dev_err(&priv->i2c->dev, |
287 | "horus3a: invalid delivery system %d\n" , |
288 | p->delivery_system); |
289 | return -EINVAL; |
290 | } |
291 | /* 0x00 - 0x04 */ |
292 | data[0] = (u8)((ms >> 7) & 0xFF); |
293 | data[1] = (u8)((ms << 1) & 0xFF); |
294 | data[2] = 0x00; |
295 | data[3] = 0x00; |
296 | data[4] = (u8)(mdiv << 7); |
297 | horus3a_write_regs(priv, reg: 0x00, data, len: sizeof(data)); |
298 | /* Write G_CTL, F_CTL */ |
299 | horus3a_write_reg(priv, reg: 0x09, val: (u8)((g_ctl << 5) | f_ctl)); |
300 | /* Write LPF cutoff frequency */ |
301 | horus3a_write_reg(priv, reg: 0x37, val: (u8)(0x80 | (fc_lpf << 1))); |
302 | /* Start Calibration */ |
303 | horus3a_write_reg(priv, reg: 0x05, val: 0x80); |
304 | /* IQ Generator enable */ |
305 | horus3a_write_reg(priv, reg: 0x2a, val: 0x7b); |
306 | /* tuner stabilization time */ |
307 | msleep(msecs: 60); |
308 | /* Store tuned frequency to the struct */ |
309 | priv->frequency = ms * 2 * 1000 / mixdiv; |
310 | return 0; |
311 | } |
312 | |
313 | static int horus3a_get_frequency(struct dvb_frontend *fe, u32 *frequency) |
314 | { |
315 | struct horus3a_priv *priv = fe->tuner_priv; |
316 | |
317 | *frequency = priv->frequency; |
318 | return 0; |
319 | } |
320 | |
321 | static const struct dvb_tuner_ops horus3a_tuner_ops = { |
322 | .info = { |
323 | .name = "Sony Horus3a" , |
324 | .frequency_min_hz = 950 * MHz, |
325 | .frequency_max_hz = 2150 * MHz, |
326 | .frequency_step_hz = 1 * MHz, |
327 | }, |
328 | .init = horus3a_init, |
329 | .release = horus3a_release, |
330 | .sleep = horus3a_sleep, |
331 | .set_params = horus3a_set_params, |
332 | .get_frequency = horus3a_get_frequency, |
333 | }; |
334 | |
335 | struct dvb_frontend *horus3a_attach(struct dvb_frontend *fe, |
336 | const struct horus3a_config *config, |
337 | struct i2c_adapter *i2c) |
338 | { |
339 | u8 buf[3], val; |
340 | struct horus3a_priv *priv = NULL; |
341 | |
342 | priv = kzalloc(size: sizeof(struct horus3a_priv), GFP_KERNEL); |
343 | if (priv == NULL) |
344 | return NULL; |
345 | priv->i2c_address = (config->i2c_address >> 1); |
346 | priv->i2c = i2c; |
347 | priv->set_tuner_data = config->set_tuner_priv; |
348 | priv->set_tuner = config->set_tuner_callback; |
349 | |
350 | if (fe->ops.i2c_gate_ctrl) |
351 | fe->ops.i2c_gate_ctrl(fe, 1); |
352 | |
353 | /* wait 4ms after power on */ |
354 | usleep_range(min: 4000, max: 6000); |
355 | /* IQ Generator disable */ |
356 | horus3a_write_reg(priv, reg: 0x2a, val: 0x79); |
357 | /* REF_R = Xtal Frequency */ |
358 | buf[0] = config->xtal_freq_mhz; |
359 | buf[1] = config->xtal_freq_mhz; |
360 | buf[2] = 0; |
361 | /* 0x6 - 0x8 */ |
362 | horus3a_write_regs(priv, reg: 0x6, data: buf, len: 3); |
363 | /* IQ Out = Single Ended */ |
364 | horus3a_write_reg(priv, reg: 0x0a, val: 0x40); |
365 | switch (config->xtal_freq_mhz) { |
366 | case 27: |
367 | val = 0x1f; |
368 | break; |
369 | case 24: |
370 | val = 0x10; |
371 | break; |
372 | case 16: |
373 | val = 0xc; |
374 | break; |
375 | default: |
376 | val = 0; |
377 | dev_warn(&priv->i2c->dev, |
378 | "horus3a: invalid xtal frequency %dMHz\n" , |
379 | config->xtal_freq_mhz); |
380 | break; |
381 | } |
382 | val <<= 2; |
383 | horus3a_write_reg(priv, reg: 0x0e, val); |
384 | horus3a_enter_power_save(priv); |
385 | usleep_range(min: 3000, max: 5000); |
386 | |
387 | if (fe->ops.i2c_gate_ctrl) |
388 | fe->ops.i2c_gate_ctrl(fe, 0); |
389 | |
390 | memcpy(&fe->ops.tuner_ops, &horus3a_tuner_ops, |
391 | sizeof(struct dvb_tuner_ops)); |
392 | fe->tuner_priv = priv; |
393 | dev_info(&priv->i2c->dev, |
394 | "Sony HORUS3A attached on addr=%x at I2C adapter %p\n" , |
395 | priv->i2c_address, priv->i2c); |
396 | return fe; |
397 | } |
398 | EXPORT_SYMBOL_GPL(horus3a_attach); |
399 | |
400 | MODULE_DESCRIPTION("Sony HORUS3A satellite tuner driver" ); |
401 | MODULE_AUTHOR("Sergey Kozlov <serjk@netup.ru>" ); |
402 | MODULE_LICENSE("GPL" ); |
403 | |