Commit | Line | Data |
---|---|---|
16216333 | 1 | // SPDX-License-Identifier: GPL-2.0-or-later |
ed85adaa AP |
2 | /* |
3 | * Elonics E4000 silicon tuner driver | |
4 | * | |
5 | * Copyright (C) 2012 Antti Palosaari <crope@iki.fi> | |
ed85adaa AP |
6 | */ |
7 | ||
8 | #include "e4000_priv.h" | |
9 | ||
c7861bb0 | 10 | static int e4000_init(struct e4000_dev *dev) |
ed85adaa | 11 | { |
f8b9b871 | 12 | struct i2c_client *client = dev->client; |
ed85adaa AP |
13 | int ret; |
14 | ||
f8b9b871 | 15 | dev_dbg(&client->dev, "\n"); |
ed85adaa AP |
16 | |
17 | /* reset */ | |
f8b9b871 | 18 | ret = regmap_write(dev->regmap, 0x00, 0x01); |
c5f51b15 | 19 | if (ret) |
ed85adaa AP |
20 | goto err; |
21 | ||
22 | /* disable output clock */ | |
f8b9b871 | 23 | ret = regmap_write(dev->regmap, 0x06, 0x00); |
c5f51b15 | 24 | if (ret) |
ed85adaa AP |
25 | goto err; |
26 | ||
f8b9b871 | 27 | ret = regmap_write(dev->regmap, 0x7a, 0x96); |
c5f51b15 | 28 | if (ret) |
ed85adaa AP |
29 | goto err; |
30 | ||
31 | /* configure gains */ | |
f8b9b871 | 32 | ret = regmap_bulk_write(dev->regmap, 0x7e, "\x01\xfe", 2); |
c5f51b15 | 33 | if (ret) |
ed85adaa AP |
34 | goto err; |
35 | ||
f8b9b871 | 36 | ret = regmap_write(dev->regmap, 0x82, 0x00); |
c5f51b15 | 37 | if (ret) |
ed85adaa AP |
38 | goto err; |
39 | ||
f8b9b871 | 40 | ret = regmap_write(dev->regmap, 0x24, 0x05); |
c5f51b15 | 41 | if (ret) |
ed85adaa AP |
42 | goto err; |
43 | ||
f8b9b871 | 44 | ret = regmap_bulk_write(dev->regmap, 0x87, "\x20\x01", 2); |
c5f51b15 | 45 | if (ret) |
ed85adaa AP |
46 | goto err; |
47 | ||
f8b9b871 | 48 | ret = regmap_bulk_write(dev->regmap, 0x9f, "\x7f\x07", 2); |
c5f51b15 | 49 | if (ret) |
ed85adaa AP |
50 | goto err; |
51 | ||
ed85adaa | 52 | /* DC offset control */ |
f8b9b871 | 53 | ret = regmap_write(dev->regmap, 0x2d, 0x1f); |
c5f51b15 | 54 | if (ret) |
85146114 AP |
55 | goto err; |
56 | ||
f8b9b871 | 57 | ret = regmap_bulk_write(dev->regmap, 0x70, "\x01\x01", 2); |
c5f51b15 | 58 | if (ret) |
ed85adaa AP |
59 | goto err; |
60 | ||
61 | /* gain control */ | |
f8b9b871 | 62 | ret = regmap_write(dev->regmap, 0x1a, 0x17); |
c5f51b15 | 63 | if (ret) |
ed85adaa AP |
64 | goto err; |
65 | ||
f8b9b871 | 66 | ret = regmap_write(dev->regmap, 0x1f, 0x1a); |
c5f51b15 | 67 | if (ret) |
ed85adaa AP |
68 | goto err; |
69 | ||
f8b9b871 | 70 | dev->active = true; |
ed85adaa | 71 | |
f8b9b871 AP |
72 | return 0; |
73 | err: | |
74 | dev_dbg(&client->dev, "failed=%d\n", ret); | |
ed85adaa AP |
75 | return ret; |
76 | } | |
77 | ||
c7861bb0 | 78 | static int e4000_sleep(struct e4000_dev *dev) |
ed85adaa | 79 | { |
f8b9b871 | 80 | struct i2c_client *client = dev->client; |
ed85adaa AP |
81 | int ret; |
82 | ||
f8b9b871 | 83 | dev_dbg(&client->dev, "\n"); |
ed85adaa | 84 | |
f8b9b871 | 85 | dev->active = false; |
ecfb7ca3 | 86 | |
f8b9b871 | 87 | ret = regmap_write(dev->regmap, 0x00, 0x00); |
c5f51b15 | 88 | if (ret) |
ed85adaa | 89 | goto err; |
ed85adaa | 90 | |
f8b9b871 AP |
91 | return 0; |
92 | err: | |
93 | dev_dbg(&client->dev, "failed=%d\n", ret); | |
ed85adaa AP |
94 | return ret; |
95 | } | |
96 | ||
c7861bb0 | 97 | static int e4000_set_params(struct e4000_dev *dev) |
ed85adaa | 98 | { |
f8b9b871 | 99 | struct i2c_client *client = dev->client; |
0e3a71c3 AP |
100 | int ret, i; |
101 | unsigned int div_n, k, k_cw, div_out; | |
0ed0b22d | 102 | u64 f_vco; |
85146114 | 103 | u8 buf[5], i_data[4], q_data[4]; |
ed85adaa | 104 | |
c7861bb0 AP |
105 | if (!dev->active) { |
106 | dev_dbg(&client->dev, "tuner is sleeping\n"); | |
107 | return 0; | |
108 | } | |
ed85adaa | 109 | |
ed85adaa | 110 | /* gain control manual */ |
f8b9b871 | 111 | ret = regmap_write(dev->regmap, 0x1a, 0x00); |
c5f51b15 | 112 | if (ret) |
ed85adaa AP |
113 | goto err; |
114 | ||
0e3a71c3 AP |
115 | /* |
116 | * Fractional-N synthesizer | |
117 | * | |
118 | * +----------------------------+ | |
119 | * v | | |
120 | * Fref +----+ +-------+ +------+ +---+ | |
121 | * ------> | PD | --> | VCO | ------> | /N.F | <-- | K | | |
122 | * +----+ +-------+ +------+ +---+ | |
123 | * | | |
124 | * | | |
125 | * v | |
126 | * +-------+ Fout | |
127 | * | /Rout | ------> | |
128 | * +-------+ | |
129 | */ | |
ed85adaa | 130 | for (i = 0; i < ARRAY_SIZE(e4000_pll_lut); i++) { |
c7861bb0 | 131 | if (dev->f_frequency <= e4000_pll_lut[i].freq) |
ed85adaa AP |
132 | break; |
133 | } | |
58f087c9 JL |
134 | if (i == ARRAY_SIZE(e4000_pll_lut)) { |
135 | ret = -EINVAL; | |
ed85adaa | 136 | goto err; |
58f087c9 | 137 | } |
ed85adaa | 138 | |
f8b9b871 | 139 | #define F_REF dev->clk |
0e3a71c3 | 140 | div_out = e4000_pll_lut[i].div_out; |
c7861bb0 | 141 | f_vco = (u64) dev->f_frequency * div_out; |
0e3a71c3 AP |
142 | /* calculate PLL integer and fractional control word */ |
143 | div_n = div_u64_rem(f_vco, F_REF, &k); | |
144 | k_cw = div_u64((u64) k * 0x10000, F_REF); | |
ed85adaa | 145 | |
f8b9b871 | 146 | dev_dbg(&client->dev, |
c7861bb0 AP |
147 | "frequency=%u bandwidth=%u f_vco=%llu F_REF=%u div_n=%u k=%u k_cw=%04x div_out=%u\n", |
148 | dev->f_frequency, dev->f_bandwidth, f_vco, F_REF, div_n, k, | |
149 | k_cw, div_out); | |
ed85adaa | 150 | |
0e3a71c3 AP |
151 | buf[0] = div_n; |
152 | buf[1] = (k_cw >> 0) & 0xff; | |
153 | buf[2] = (k_cw >> 8) & 0xff; | |
154 | buf[3] = 0x00; | |
155 | buf[4] = e4000_pll_lut[i].div_out_reg; | |
f8b9b871 | 156 | ret = regmap_bulk_write(dev->regmap, 0x09, buf, 5); |
c5f51b15 | 157 | if (ret) |
ed85adaa AP |
158 | goto err; |
159 | ||
160 | /* LNA filter (RF filter) */ | |
161 | for (i = 0; i < ARRAY_SIZE(e400_lna_filter_lut); i++) { | |
c7861bb0 | 162 | if (dev->f_frequency <= e400_lna_filter_lut[i].freq) |
ed85adaa AP |
163 | break; |
164 | } | |
58f087c9 JL |
165 | if (i == ARRAY_SIZE(e400_lna_filter_lut)) { |
166 | ret = -EINVAL; | |
ed85adaa | 167 | goto err; |
58f087c9 | 168 | } |
ed85adaa | 169 | |
f8b9b871 | 170 | ret = regmap_write(dev->regmap, 0x10, e400_lna_filter_lut[i].val); |
c5f51b15 | 171 | if (ret) |
ed85adaa AP |
172 | goto err; |
173 | ||
174 | /* IF filters */ | |
175 | for (i = 0; i < ARRAY_SIZE(e4000_if_filter_lut); i++) { | |
c7861bb0 | 176 | if (dev->f_bandwidth <= e4000_if_filter_lut[i].freq) |
ed85adaa AP |
177 | break; |
178 | } | |
58f087c9 JL |
179 | if (i == ARRAY_SIZE(e4000_if_filter_lut)) { |
180 | ret = -EINVAL; | |
ed85adaa | 181 | goto err; |
58f087c9 | 182 | } |
ed85adaa AP |
183 | |
184 | buf[0] = e4000_if_filter_lut[i].reg11_val; | |
185 | buf[1] = e4000_if_filter_lut[i].reg12_val; | |
186 | ||
f8b9b871 | 187 | ret = regmap_bulk_write(dev->regmap, 0x11, buf, 2); |
c5f51b15 | 188 | if (ret) |
ed85adaa AP |
189 | goto err; |
190 | ||
191 | /* frequency band */ | |
192 | for (i = 0; i < ARRAY_SIZE(e4000_band_lut); i++) { | |
c7861bb0 | 193 | if (dev->f_frequency <= e4000_band_lut[i].freq) |
ed85adaa AP |
194 | break; |
195 | } | |
58f087c9 JL |
196 | if (i == ARRAY_SIZE(e4000_band_lut)) { |
197 | ret = -EINVAL; | |
ed85adaa | 198 | goto err; |
58f087c9 | 199 | } |
ed85adaa | 200 | |
f8b9b871 | 201 | ret = regmap_write(dev->regmap, 0x07, e4000_band_lut[i].reg07_val); |
c5f51b15 | 202 | if (ret) |
ed85adaa AP |
203 | goto err; |
204 | ||
f8b9b871 | 205 | ret = regmap_write(dev->regmap, 0x78, e4000_band_lut[i].reg78_val); |
c5f51b15 | 206 | if (ret) |
ed85adaa AP |
207 | goto err; |
208 | ||
85146114 AP |
209 | /* DC offset */ |
210 | for (i = 0; i < 4; i++) { | |
211 | if (i == 0) | |
f8b9b871 | 212 | ret = regmap_bulk_write(dev->regmap, 0x15, "\x00\x7e\x24", 3); |
85146114 | 213 | else if (i == 1) |
f8b9b871 | 214 | ret = regmap_bulk_write(dev->regmap, 0x15, "\x00\x7f", 2); |
85146114 | 215 | else if (i == 2) |
f8b9b871 | 216 | ret = regmap_bulk_write(dev->regmap, 0x15, "\x01", 1); |
85146114 | 217 | else |
f8b9b871 | 218 | ret = regmap_bulk_write(dev->regmap, 0x16, "\x7e", 1); |
85146114 | 219 | |
c5f51b15 | 220 | if (ret) |
85146114 AP |
221 | goto err; |
222 | ||
f8b9b871 | 223 | ret = regmap_write(dev->regmap, 0x29, 0x01); |
c5f51b15 | 224 | if (ret) |
85146114 AP |
225 | goto err; |
226 | ||
f8b9b871 | 227 | ret = regmap_bulk_read(dev->regmap, 0x2a, buf, 3); |
c5f51b15 | 228 | if (ret) |
85146114 AP |
229 | goto err; |
230 | ||
231 | i_data[i] = (((buf[2] >> 0) & 0x3) << 6) | (buf[0] & 0x3f); | |
232 | q_data[i] = (((buf[2] >> 4) & 0x3) << 6) | (buf[1] & 0x3f); | |
233 | } | |
234 | ||
d4992da3 AP |
235 | swap(q_data[2], q_data[3]); |
236 | swap(i_data[2], i_data[3]); | |
237 | ||
f8b9b871 | 238 | ret = regmap_bulk_write(dev->regmap, 0x50, q_data, 4); |
c5f51b15 | 239 | if (ret) |
85146114 AP |
240 | goto err; |
241 | ||
f8b9b871 | 242 | ret = regmap_bulk_write(dev->regmap, 0x60, i_data, 4); |
c5f51b15 | 243 | if (ret) |
85146114 AP |
244 | goto err; |
245 | ||
ed85adaa | 246 | /* gain control auto */ |
f8b9b871 | 247 | ret = regmap_write(dev->regmap, 0x1a, 0x17); |
c5f51b15 | 248 | if (ret) |
ed85adaa | 249 | goto err; |
ed85adaa | 250 | |
f8b9b871 AP |
251 | return 0; |
252 | err: | |
253 | dev_dbg(&client->dev, "failed=%d\n", ret); | |
ed85adaa AP |
254 | return ret; |
255 | } | |
256 | ||
c7861bb0 AP |
257 | /* |
258 | * V4L2 API | |
259 | */ | |
260 | #if IS_ENABLED(CONFIG_VIDEO_V4L2) | |
261 | static const struct v4l2_frequency_band bands[] = { | |
262 | { | |
263 | .type = V4L2_TUNER_RF, | |
264 | .index = 0, | |
265 | .capability = V4L2_TUNER_CAP_1HZ | V4L2_TUNER_CAP_FREQ_BANDS, | |
266 | .rangelow = 59000000, | |
267 | .rangehigh = 1105000000, | |
268 | }, | |
269 | { | |
270 | .type = V4L2_TUNER_RF, | |
271 | .index = 1, | |
272 | .capability = V4L2_TUNER_CAP_1HZ | V4L2_TUNER_CAP_FREQ_BANDS, | |
273 | .rangelow = 1249000000, | |
1ba90492 | 274 | .rangehigh = 2208000000UL, |
c7861bb0 AP |
275 | }, |
276 | }; | |
277 | ||
278 | static inline struct e4000_dev *e4000_subdev_to_dev(struct v4l2_subdev *sd) | |
ed85adaa | 279 | { |
c7861bb0 AP |
280 | return container_of(sd, struct e4000_dev, sd); |
281 | } | |
282 | ||
3aab15af | 283 | static int e4000_standby(struct v4l2_subdev *sd) |
c7861bb0 AP |
284 | { |
285 | struct e4000_dev *dev = e4000_subdev_to_dev(sd); | |
c7861bb0 | 286 | int ret; |
ed85adaa | 287 | |
3aab15af | 288 | ret = e4000_sleep(dev); |
c7861bb0 AP |
289 | if (ret) |
290 | return ret; | |
291 | ||
292 | return e4000_set_params(dev); | |
293 | } | |
294 | ||
c7861bb0 AP |
295 | static int e4000_g_tuner(struct v4l2_subdev *sd, struct v4l2_tuner *v) |
296 | { | |
297 | struct e4000_dev *dev = e4000_subdev_to_dev(sd); | |
298 | struct i2c_client *client = dev->client; | |
299 | ||
300 | dev_dbg(&client->dev, "index=%d\n", v->index); | |
301 | ||
c0decac1 | 302 | strscpy(v->name, "Elonics E4000", sizeof(v->name)); |
c7861bb0 AP |
303 | v->type = V4L2_TUNER_RF; |
304 | v->capability = V4L2_TUNER_CAP_1HZ | V4L2_TUNER_CAP_FREQ_BANDS; | |
305 | v->rangelow = bands[0].rangelow; | |
306 | v->rangehigh = bands[1].rangehigh; | |
ed85adaa AP |
307 | return 0; |
308 | } | |
309 | ||
c7861bb0 AP |
310 | static int e4000_s_tuner(struct v4l2_subdev *sd, const struct v4l2_tuner *v) |
311 | { | |
312 | struct e4000_dev *dev = e4000_subdev_to_dev(sd); | |
313 | struct i2c_client *client = dev->client; | |
314 | ||
315 | dev_dbg(&client->dev, "index=%d\n", v->index); | |
316 | return 0; | |
317 | } | |
318 | ||
319 | static int e4000_g_frequency(struct v4l2_subdev *sd, struct v4l2_frequency *f) | |
320 | { | |
321 | struct e4000_dev *dev = e4000_subdev_to_dev(sd); | |
322 | struct i2c_client *client = dev->client; | |
323 | ||
324 | dev_dbg(&client->dev, "tuner=%d\n", f->tuner); | |
325 | f->frequency = dev->f_frequency; | |
326 | return 0; | |
327 | } | |
328 | ||
329 | static int e4000_s_frequency(struct v4l2_subdev *sd, | |
330 | const struct v4l2_frequency *f) | |
331 | { | |
332 | struct e4000_dev *dev = e4000_subdev_to_dev(sd); | |
333 | struct i2c_client *client = dev->client; | |
334 | ||
335 | dev_dbg(&client->dev, "tuner=%d type=%d frequency=%u\n", | |
336 | f->tuner, f->type, f->frequency); | |
337 | ||
338 | dev->f_frequency = clamp_t(unsigned int, f->frequency, | |
339 | bands[0].rangelow, bands[1].rangehigh); | |
340 | return e4000_set_params(dev); | |
341 | } | |
342 | ||
343 | static int e4000_enum_freq_bands(struct v4l2_subdev *sd, | |
344 | struct v4l2_frequency_band *band) | |
345 | { | |
346 | struct e4000_dev *dev = e4000_subdev_to_dev(sd); | |
347 | struct i2c_client *client = dev->client; | |
348 | ||
349 | dev_dbg(&client->dev, "tuner=%d type=%d index=%d\n", | |
350 | band->tuner, band->type, band->index); | |
351 | ||
352 | if (band->index >= ARRAY_SIZE(bands)) | |
353 | return -EINVAL; | |
354 | ||
355 | band->capability = bands[band->index].capability; | |
356 | band->rangelow = bands[band->index].rangelow; | |
357 | band->rangehigh = bands[band->index].rangehigh; | |
358 | return 0; | |
359 | } | |
360 | ||
361 | static const struct v4l2_subdev_tuner_ops e4000_subdev_tuner_ops = { | |
3aab15af | 362 | .standby = e4000_standby, |
c7861bb0 AP |
363 | .g_tuner = e4000_g_tuner, |
364 | .s_tuner = e4000_s_tuner, | |
365 | .g_frequency = e4000_g_frequency, | |
366 | .s_frequency = e4000_s_frequency, | |
367 | .enum_freq_bands = e4000_enum_freq_bands, | |
368 | }; | |
369 | ||
370 | static const struct v4l2_subdev_ops e4000_subdev_ops = { | |
c7861bb0 AP |
371 | .tuner = &e4000_subdev_tuner_ops, |
372 | }; | |
373 | ||
adaa616f AP |
374 | static int e4000_set_lna_gain(struct dvb_frontend *fe) |
375 | { | |
f8b9b871 AP |
376 | struct e4000_dev *dev = fe->tuner_priv; |
377 | struct i2c_client *client = dev->client; | |
adaa616f AP |
378 | int ret; |
379 | u8 u8tmp; | |
1c73fc6b | 380 | |
f8b9b871 AP |
381 | dev_dbg(&client->dev, "lna auto=%d->%d val=%d->%d\n", |
382 | dev->lna_gain_auto->cur.val, dev->lna_gain_auto->val, | |
383 | dev->lna_gain->cur.val, dev->lna_gain->val); | |
adaa616f | 384 | |
f8b9b871 | 385 | if (dev->lna_gain_auto->val && dev->if_gain_auto->cur.val) |
adaa616f | 386 | u8tmp = 0x17; |
f8b9b871 | 387 | else if (dev->lna_gain_auto->val) |
adaa616f | 388 | u8tmp = 0x19; |
f8b9b871 | 389 | else if (dev->if_gain_auto->cur.val) |
adaa616f AP |
390 | u8tmp = 0x16; |
391 | else | |
392 | u8tmp = 0x10; | |
393 | ||
f8b9b871 | 394 | ret = regmap_write(dev->regmap, 0x1a, u8tmp); |
adaa616f AP |
395 | if (ret) |
396 | goto err; | |
397 | ||
f8b9b871 AP |
398 | if (dev->lna_gain_auto->val == false) { |
399 | ret = regmap_write(dev->regmap, 0x14, dev->lna_gain->val); | |
adaa616f AP |
400 | if (ret) |
401 | goto err; | |
402 | } | |
adaa616f | 403 | |
f8b9b871 AP |
404 | return 0; |
405 | err: | |
406 | dev_dbg(&client->dev, "failed=%d\n", ret); | |
adaa616f AP |
407 | return ret; |
408 | } | |
409 | ||
410 | static int e4000_set_mixer_gain(struct dvb_frontend *fe) | |
411 | { | |
f8b9b871 AP |
412 | struct e4000_dev *dev = fe->tuner_priv; |
413 | struct i2c_client *client = dev->client; | |
adaa616f AP |
414 | int ret; |
415 | u8 u8tmp; | |
1c73fc6b | 416 | |
f8b9b871 AP |
417 | dev_dbg(&client->dev, "mixer auto=%d->%d val=%d->%d\n", |
418 | dev->mixer_gain_auto->cur.val, dev->mixer_gain_auto->val, | |
419 | dev->mixer_gain->cur.val, dev->mixer_gain->val); | |
adaa616f | 420 | |
f8b9b871 | 421 | if (dev->mixer_gain_auto->val) |
adaa616f AP |
422 | u8tmp = 0x15; |
423 | else | |
424 | u8tmp = 0x14; | |
425 | ||
f8b9b871 | 426 | ret = regmap_write(dev->regmap, 0x20, u8tmp); |
adaa616f AP |
427 | if (ret) |
428 | goto err; | |
429 | ||
f8b9b871 AP |
430 | if (dev->mixer_gain_auto->val == false) { |
431 | ret = regmap_write(dev->regmap, 0x15, dev->mixer_gain->val); | |
adaa616f AP |
432 | if (ret) |
433 | goto err; | |
434 | } | |
adaa616f | 435 | |
f8b9b871 AP |
436 | return 0; |
437 | err: | |
438 | dev_dbg(&client->dev, "failed=%d\n", ret); | |
adaa616f AP |
439 | return ret; |
440 | } | |
441 | ||
442 | static int e4000_set_if_gain(struct dvb_frontend *fe) | |
443 | { | |
f8b9b871 AP |
444 | struct e4000_dev *dev = fe->tuner_priv; |
445 | struct i2c_client *client = dev->client; | |
adaa616f AP |
446 | int ret; |
447 | u8 buf[2]; | |
448 | u8 u8tmp; | |
1c73fc6b | 449 | |
f8b9b871 AP |
450 | dev_dbg(&client->dev, "if auto=%d->%d val=%d->%d\n", |
451 | dev->if_gain_auto->cur.val, dev->if_gain_auto->val, | |
452 | dev->if_gain->cur.val, dev->if_gain->val); | |
adaa616f | 453 | |
f8b9b871 | 454 | if (dev->if_gain_auto->val && dev->lna_gain_auto->cur.val) |
adaa616f | 455 | u8tmp = 0x17; |
f8b9b871 | 456 | else if (dev->lna_gain_auto->cur.val) |
adaa616f | 457 | u8tmp = 0x19; |
f8b9b871 | 458 | else if (dev->if_gain_auto->val) |
adaa616f AP |
459 | u8tmp = 0x16; |
460 | else | |
461 | u8tmp = 0x10; | |
462 | ||
f8b9b871 | 463 | ret = regmap_write(dev->regmap, 0x1a, u8tmp); |
adaa616f AP |
464 | if (ret) |
465 | goto err; | |
466 | ||
f8b9b871 AP |
467 | if (dev->if_gain_auto->val == false) { |
468 | buf[0] = e4000_if_gain_lut[dev->if_gain->val].reg16_val; | |
469 | buf[1] = e4000_if_gain_lut[dev->if_gain->val].reg17_val; | |
470 | ret = regmap_bulk_write(dev->regmap, 0x16, buf, 2); | |
adaa616f AP |
471 | if (ret) |
472 | goto err; | |
473 | } | |
adaa616f | 474 | |
f8b9b871 AP |
475 | return 0; |
476 | err: | |
477 | dev_dbg(&client->dev, "failed=%d\n", ret); | |
adaa616f AP |
478 | return ret; |
479 | } | |
480 | ||
ecfb7ca3 AP |
481 | static int e4000_pll_lock(struct dvb_frontend *fe) |
482 | { | |
f8b9b871 AP |
483 | struct e4000_dev *dev = fe->tuner_priv; |
484 | struct i2c_client *client = dev->client; | |
ecfb7ca3 | 485 | int ret; |
f8b9b871 | 486 | unsigned int uitmp; |
ecfb7ca3 | 487 | |
f8b9b871 | 488 | ret = regmap_read(dev->regmap, 0x07, &uitmp); |
c5f51b15 | 489 | if (ret) |
ecfb7ca3 AP |
490 | goto err; |
491 | ||
f8b9b871 | 492 | dev->pll_lock->val = (uitmp & 0x01); |
ecfb7ca3 | 493 | |
f8b9b871 AP |
494 | return 0; |
495 | err: | |
496 | dev_dbg(&client->dev, "failed=%d\n", ret); | |
ecfb7ca3 AP |
497 | return ret; |
498 | } | |
499 | ||
500 | static int e4000_g_volatile_ctrl(struct v4l2_ctrl *ctrl) | |
501 | { | |
f8b9b871 AP |
502 | struct e4000_dev *dev = container_of(ctrl->handler, struct e4000_dev, hdl); |
503 | struct i2c_client *client = dev->client; | |
ecfb7ca3 AP |
504 | int ret; |
505 | ||
f8b9b871 | 506 | if (!dev->active) |
bd428bbc AP |
507 | return 0; |
508 | ||
ecfb7ca3 AP |
509 | switch (ctrl->id) { |
510 | case V4L2_CID_RF_TUNER_PLL_LOCK: | |
f8b9b871 | 511 | ret = e4000_pll_lock(dev->fe); |
ecfb7ca3 AP |
512 | break; |
513 | default: | |
f8b9b871 AP |
514 | dev_dbg(&client->dev, "unknown ctrl: id=%d name=%s\n", |
515 | ctrl->id, ctrl->name); | |
ecfb7ca3 AP |
516 | ret = -EINVAL; |
517 | } | |
518 | ||
519 | return ret; | |
520 | } | |
521 | ||
adaa616f AP |
522 | static int e4000_s_ctrl(struct v4l2_ctrl *ctrl) |
523 | { | |
f8b9b871 AP |
524 | struct e4000_dev *dev = container_of(ctrl->handler, struct e4000_dev, hdl); |
525 | struct i2c_client *client = dev->client; | |
adaa616f | 526 | int ret; |
1c73fc6b | 527 | |
f8b9b871 | 528 | if (!dev->active) |
bd428bbc | 529 | return 0; |
adaa616f AP |
530 | |
531 | switch (ctrl->id) { | |
532 | case V4L2_CID_RF_TUNER_BANDWIDTH_AUTO: | |
533 | case V4L2_CID_RF_TUNER_BANDWIDTH: | |
c7861bb0 AP |
534 | /* |
535 | * TODO: Auto logic does not work 100% correctly as tuner driver | |
536 | * do not have information to calculate maximum suitable | |
537 | * bandwidth. Calculating it is responsible of master driver. | |
538 | */ | |
539 | dev->f_bandwidth = dev->bandwidth->val; | |
540 | ret = e4000_set_params(dev); | |
adaa616f AP |
541 | break; |
542 | case V4L2_CID_RF_TUNER_LNA_GAIN_AUTO: | |
543 | case V4L2_CID_RF_TUNER_LNA_GAIN: | |
f8b9b871 | 544 | ret = e4000_set_lna_gain(dev->fe); |
adaa616f AP |
545 | break; |
546 | case V4L2_CID_RF_TUNER_MIXER_GAIN_AUTO: | |
547 | case V4L2_CID_RF_TUNER_MIXER_GAIN: | |
f8b9b871 | 548 | ret = e4000_set_mixer_gain(dev->fe); |
adaa616f AP |
549 | break; |
550 | case V4L2_CID_RF_TUNER_IF_GAIN_AUTO: | |
551 | case V4L2_CID_RF_TUNER_IF_GAIN: | |
f8b9b871 | 552 | ret = e4000_set_if_gain(dev->fe); |
adaa616f AP |
553 | break; |
554 | default: | |
f8b9b871 AP |
555 | dev_dbg(&client->dev, "unknown ctrl: id=%d name=%s\n", |
556 | ctrl->id, ctrl->name); | |
adaa616f AP |
557 | ret = -EINVAL; |
558 | } | |
559 | ||
560 | return ret; | |
561 | } | |
562 | ||
563 | static const struct v4l2_ctrl_ops e4000_ctrl_ops = { | |
ecfb7ca3 | 564 | .g_volatile_ctrl = e4000_g_volatile_ctrl, |
adaa616f AP |
565 | .s_ctrl = e4000_s_ctrl, |
566 | }; | |
320c6387 | 567 | #endif |
adaa616f | 568 | |
c7861bb0 AP |
569 | /* |
570 | * DVB API | |
571 | */ | |
572 | static int e4000_dvb_set_params(struct dvb_frontend *fe) | |
573 | { | |
574 | struct e4000_dev *dev = fe->tuner_priv; | |
575 | struct dtv_frontend_properties *c = &fe->dtv_property_cache; | |
576 | ||
577 | dev->f_frequency = c->frequency; | |
578 | dev->f_bandwidth = c->bandwidth_hz; | |
579 | return e4000_set_params(dev); | |
580 | } | |
581 | ||
582 | static int e4000_dvb_init(struct dvb_frontend *fe) | |
583 | { | |
584 | return e4000_init(fe->tuner_priv); | |
585 | } | |
586 | ||
587 | static int e4000_dvb_sleep(struct dvb_frontend *fe) | |
588 | { | |
589 | return e4000_sleep(fe->tuner_priv); | |
590 | } | |
591 | ||
592 | static int e4000_dvb_get_if_frequency(struct dvb_frontend *fe, u32 *frequency) | |
593 | { | |
594 | *frequency = 0; /* Zero-IF */ | |
595 | return 0; | |
596 | } | |
597 | ||
598 | static const struct dvb_tuner_ops e4000_dvb_tuner_ops = { | |
ed85adaa | 599 | .info = { |
a3f90c75 MCC |
600 | .name = "Elonics E4000", |
601 | .frequency_min_hz = 174 * MHz, | |
602 | .frequency_max_hz = 862 * MHz, | |
ed85adaa AP |
603 | }, |
604 | ||
c7861bb0 AP |
605 | .init = e4000_dvb_init, |
606 | .sleep = e4000_dvb_sleep, | |
607 | .set_params = e4000_dvb_set_params, | |
ed85adaa | 608 | |
c7861bb0 | 609 | .get_if_frequency = e4000_dvb_get_if_frequency, |
ed85adaa AP |
610 | }; |
611 | ||
28fd31f8 | 612 | static int e4000_probe(struct i2c_client *client, |
f8b9b871 | 613 | const struct i2c_device_id *id) |
ed85adaa | 614 | { |
f8b9b871 | 615 | struct e4000_dev *dev; |
28fd31f8 AP |
616 | struct e4000_config *cfg = client->dev.platform_data; |
617 | struct dvb_frontend *fe = cfg->fe; | |
ed85adaa | 618 | int ret; |
f8b9b871 | 619 | unsigned int uitmp; |
bd428bbc AP |
620 | static const struct regmap_config regmap_config = { |
621 | .reg_bits = 8, | |
622 | .val_bits = 8, | |
bd428bbc | 623 | }; |
ed85adaa | 624 | |
f8b9b871 AP |
625 | dev = kzalloc(sizeof(*dev), GFP_KERNEL); |
626 | if (!dev) { | |
ed85adaa | 627 | ret = -ENOMEM; |
ed85adaa AP |
628 | goto err; |
629 | } | |
630 | ||
f8b9b871 AP |
631 | dev->clk = cfg->clock; |
632 | dev->client = client; | |
633 | dev->fe = cfg->fe; | |
634 | dev->regmap = devm_regmap_init_i2c(client, ®map_config); | |
635 | if (IS_ERR(dev->regmap)) { | |
636 | ret = PTR_ERR(dev->regmap); | |
637 | goto err_kfree; | |
bd428bbc | 638 | } |
ed85adaa AP |
639 | |
640 | /* check if the tuner is there */ | |
f8b9b871 | 641 | ret = regmap_read(dev->regmap, 0x02, &uitmp); |
c5f51b15 | 642 | if (ret) |
f8b9b871 | 643 | goto err_kfree; |
ed85adaa | 644 | |
f8b9b871 | 645 | dev_dbg(&client->dev, "chip id=%02x\n", uitmp); |
ed85adaa | 646 | |
f8b9b871 | 647 | if (uitmp != 0x40) { |
28fd31f8 | 648 | ret = -ENODEV; |
f8b9b871 | 649 | goto err_kfree; |
28fd31f8 | 650 | } |
ed85adaa AP |
651 | |
652 | /* put sleep as chip seems to be in normal mode by default */ | |
f8b9b871 | 653 | ret = regmap_write(dev->regmap, 0x00, 0x00); |
c5f51b15 | 654 | if (ret) |
f8b9b871 | 655 | goto err_kfree; |
ed85adaa | 656 | |
320c6387 | 657 | #if IS_ENABLED(CONFIG_VIDEO_V4L2) |
adaa616f | 658 | /* Register controls */ |
f8b9b871 AP |
659 | v4l2_ctrl_handler_init(&dev->hdl, 9); |
660 | dev->bandwidth_auto = v4l2_ctrl_new_std(&dev->hdl, &e4000_ctrl_ops, | |
adaa616f | 661 | V4L2_CID_RF_TUNER_BANDWIDTH_AUTO, 0, 1, 1, 1); |
f8b9b871 | 662 | dev->bandwidth = v4l2_ctrl_new_std(&dev->hdl, &e4000_ctrl_ops, |
adaa616f | 663 | V4L2_CID_RF_TUNER_BANDWIDTH, 4300000, 11000000, 100000, 4300000); |
f8b9b871 AP |
664 | v4l2_ctrl_auto_cluster(2, &dev->bandwidth_auto, 0, false); |
665 | dev->lna_gain_auto = v4l2_ctrl_new_std(&dev->hdl, &e4000_ctrl_ops, | |
adaa616f | 666 | V4L2_CID_RF_TUNER_LNA_GAIN_AUTO, 0, 1, 1, 1); |
f8b9b871 | 667 | dev->lna_gain = v4l2_ctrl_new_std(&dev->hdl, &e4000_ctrl_ops, |
adaa616f | 668 | V4L2_CID_RF_TUNER_LNA_GAIN, 0, 15, 1, 10); |
f8b9b871 AP |
669 | v4l2_ctrl_auto_cluster(2, &dev->lna_gain_auto, 0, false); |
670 | dev->mixer_gain_auto = v4l2_ctrl_new_std(&dev->hdl, &e4000_ctrl_ops, | |
adaa616f | 671 | V4L2_CID_RF_TUNER_MIXER_GAIN_AUTO, 0, 1, 1, 1); |
f8b9b871 | 672 | dev->mixer_gain = v4l2_ctrl_new_std(&dev->hdl, &e4000_ctrl_ops, |
adaa616f | 673 | V4L2_CID_RF_TUNER_MIXER_GAIN, 0, 1, 1, 1); |
f8b9b871 AP |
674 | v4l2_ctrl_auto_cluster(2, &dev->mixer_gain_auto, 0, false); |
675 | dev->if_gain_auto = v4l2_ctrl_new_std(&dev->hdl, &e4000_ctrl_ops, | |
adaa616f | 676 | V4L2_CID_RF_TUNER_IF_GAIN_AUTO, 0, 1, 1, 1); |
f8b9b871 | 677 | dev->if_gain = v4l2_ctrl_new_std(&dev->hdl, &e4000_ctrl_ops, |
adaa616f | 678 | V4L2_CID_RF_TUNER_IF_GAIN, 0, 54, 1, 0); |
f8b9b871 AP |
679 | v4l2_ctrl_auto_cluster(2, &dev->if_gain_auto, 0, false); |
680 | dev->pll_lock = v4l2_ctrl_new_std(&dev->hdl, &e4000_ctrl_ops, | |
ecfb7ca3 | 681 | V4L2_CID_RF_TUNER_PLL_LOCK, 0, 1, 1, 0); |
f8b9b871 AP |
682 | if (dev->hdl.error) { |
683 | ret = dev->hdl.error; | |
684 | dev_err(&client->dev, "Could not initialize controls\n"); | |
685 | v4l2_ctrl_handler_free(&dev->hdl); | |
686 | goto err_kfree; | |
adaa616f AP |
687 | } |
688 | ||
f8b9b871 | 689 | dev->sd.ctrl_handler = &dev->hdl; |
c7861bb0 AP |
690 | dev->f_frequency = bands[0].rangelow; |
691 | dev->f_bandwidth = dev->bandwidth->val; | |
692 | v4l2_i2c_subdev_init(&dev->sd, client, &e4000_subdev_ops); | |
320c6387 | 693 | #endif |
f8b9b871 | 694 | fe->tuner_priv = dev; |
c7861bb0 AP |
695 | memcpy(&fe->ops.tuner_ops, &e4000_dvb_tuner_ops, |
696 | sizeof(fe->ops.tuner_ops)); | |
f8b9b871 AP |
697 | v4l2_set_subdevdata(&dev->sd, client); |
698 | i2c_set_clientdata(client, &dev->sd); | |
36f647ba | 699 | |
f8b9b871 | 700 | dev_info(&client->dev, "Elonics E4000 successfully identified\n"); |
28fd31f8 | 701 | return 0; |
f8b9b871 AP |
702 | err_kfree: |
703 | kfree(dev); | |
ed85adaa | 704 | err: |
f8b9b871 | 705 | dev_dbg(&client->dev, "failed=%d\n", ret); |
28fd31f8 | 706 | return ret; |
ed85adaa | 707 | } |
28fd31f8 AP |
708 | |
709 | static int e4000_remove(struct i2c_client *client) | |
710 | { | |
adaa616f | 711 | struct v4l2_subdev *sd = i2c_get_clientdata(client); |
f8b9b871 | 712 | struct e4000_dev *dev = container_of(sd, struct e4000_dev, sd); |
28fd31f8 | 713 | |
13bd82d1 | 714 | dev_dbg(&client->dev, "\n"); |
1c73fc6b | 715 | |
320c6387 | 716 | #if IS_ENABLED(CONFIG_VIDEO_V4L2) |
f8b9b871 | 717 | v4l2_ctrl_handler_free(&dev->hdl); |
320c6387 | 718 | #endif |
f8b9b871 | 719 | kfree(dev); |
28fd31f8 AP |
720 | |
721 | return 0; | |
722 | } | |
723 | ||
f8b9b871 | 724 | static const struct i2c_device_id e4000_id_table[] = { |
28fd31f8 AP |
725 | {"e4000", 0}, |
726 | {} | |
727 | }; | |
f8b9b871 | 728 | MODULE_DEVICE_TABLE(i2c, e4000_id_table); |
28fd31f8 AP |
729 | |
730 | static struct i2c_driver e4000_driver = { | |
731 | .driver = { | |
28fd31f8 | 732 | .name = "e4000", |
f8b9b871 | 733 | .suppress_bind_attrs = true, |
28fd31f8 AP |
734 | }, |
735 | .probe = e4000_probe, | |
736 | .remove = e4000_remove, | |
f8b9b871 | 737 | .id_table = e4000_id_table, |
28fd31f8 AP |
738 | }; |
739 | ||
740 | module_i2c_driver(e4000_driver); | |
ed85adaa AP |
741 | |
742 | MODULE_DESCRIPTION("Elonics E4000 silicon tuner driver"); | |
743 | MODULE_AUTHOR("Antti Palosaari <crope@iki.fi>"); | |
744 | MODULE_LICENSE("GPL"); |