diff options
author | Michael Büsch <m@bues.ch> | 2012-04-02 11:14:32 -0400 |
---|---|---|
committer | Mauro Carvalho Chehab <mchehab@redhat.com> | 2012-04-09 13:49:32 -0400 |
commit | eea977ed63c16888a87acd12958966638ac4fb3a (patch) | |
tree | 151c4fd38b2812d6afcfd677f02ecf304cc5c2db | |
parent | 6ec12988c022bee4af28a5d3941a01f9e78a96e9 (diff) |
[media] Add fc0011 tuner driver
This adds support for the Fitipower fc0011 DVB-t tuner.
Signed-off-by: Michael Buesch <m@bues.ch>
Signed-off-by: Antti Palosaari <crope@iki.fi>
Signed-off-by: Mauro Carvalho Chehab <mchehab@redhat.com>
-rw-r--r-- | MAINTAINERS | 7 | ||||
-rw-r--r-- | drivers/media/common/tuners/Kconfig | 7 | ||||
-rw-r--r-- | drivers/media/common/tuners/Makefile | 1 | ||||
-rw-r--r-- | drivers/media/common/tuners/fc0011.c | 524 | ||||
-rw-r--r-- | drivers/media/common/tuners/fc0011.h | 41 |
5 files changed, 580 insertions, 0 deletions
diff --git a/MAINTAINERS b/MAINTAINERS index 699546996559..9e05ceb2921f 100644 --- a/MAINTAINERS +++ b/MAINTAINERS | |||
@@ -2692,6 +2692,13 @@ S: Maintained | |||
2692 | F: Documentation/hwmon/f71805f | 2692 | F: Documentation/hwmon/f71805f |
2693 | F: drivers/hwmon/f71805f.c | 2693 | F: drivers/hwmon/f71805f.c |
2694 | 2694 | ||
2695 | FC0011 TUNER DRIVER | ||
2696 | M: Michael Buesch <m@bues.ch> | ||
2697 | L: linux-media@vger.kernel.org | ||
2698 | S: Maintained | ||
2699 | F: drivers/media/common/tuners/fc0011.h | ||
2700 | F: drivers/media/common/tuners/fc0011.c | ||
2701 | |||
2695 | FANOTIFY | 2702 | FANOTIFY |
2696 | M: Eric Paris <eparis@redhat.com> | 2703 | M: Eric Paris <eparis@redhat.com> |
2697 | S: Maintained | 2704 | S: Maintained |
diff --git a/drivers/media/common/tuners/Kconfig b/drivers/media/common/tuners/Kconfig index ae8ebcfa6fa2..0fd15d925e15 100644 --- a/drivers/media/common/tuners/Kconfig +++ b/drivers/media/common/tuners/Kconfig | |||
@@ -204,6 +204,13 @@ config MEDIA_TUNER_TDA18218 | |||
204 | help | 204 | help |
205 | NXP TDA18218 silicon tuner driver. | 205 | NXP TDA18218 silicon tuner driver. |
206 | 206 | ||
207 | config MEDIA_TUNER_FC0011 | ||
208 | tristate "Fitipower FC0011 silicon tuner" | ||
209 | depends on VIDEO_MEDIA && I2C | ||
210 | default m if MEDIA_TUNER_CUSTOMISE | ||
211 | help | ||
212 | Fitipower FC0011 silicon tuner driver. | ||
213 | |||
207 | config MEDIA_TUNER_TDA18212 | 214 | config MEDIA_TUNER_TDA18212 |
208 | tristate "NXP TDA18212 silicon tuner" | 215 | tristate "NXP TDA18212 silicon tuner" |
209 | depends on VIDEO_MEDIA && I2C | 216 | depends on VIDEO_MEDIA && I2C |
diff --git a/drivers/media/common/tuners/Makefile b/drivers/media/common/tuners/Makefile index 6c3040501c45..64ee06fa83f1 100644 --- a/drivers/media/common/tuners/Makefile +++ b/drivers/media/common/tuners/Makefile | |||
@@ -29,6 +29,7 @@ obj-$(CONFIG_MEDIA_TUNER_MAX2165) += max2165.o | |||
29 | obj-$(CONFIG_MEDIA_TUNER_TDA18218) += tda18218.o | 29 | obj-$(CONFIG_MEDIA_TUNER_TDA18218) += tda18218.o |
30 | obj-$(CONFIG_MEDIA_TUNER_TDA18212) += tda18212.o | 30 | obj-$(CONFIG_MEDIA_TUNER_TDA18212) += tda18212.o |
31 | obj-$(CONFIG_MEDIA_TUNER_TUA9001) += tua9001.o | 31 | obj-$(CONFIG_MEDIA_TUNER_TUA9001) += tua9001.o |
32 | obj-$(CONFIG_MEDIA_TUNER_FC0011) += fc0011.o | ||
32 | 33 | ||
33 | ccflags-y += -I$(srctree)/drivers/media/dvb/dvb-core | 34 | ccflags-y += -I$(srctree)/drivers/media/dvb/dvb-core |
34 | ccflags-y += -I$(srctree)/drivers/media/dvb/frontends | 35 | ccflags-y += -I$(srctree)/drivers/media/dvb/frontends |
diff --git a/drivers/media/common/tuners/fc0011.c b/drivers/media/common/tuners/fc0011.c new file mode 100644 index 000000000000..7842a4eee3d7 --- /dev/null +++ b/drivers/media/common/tuners/fc0011.c | |||
@@ -0,0 +1,524 @@ | |||
1 | /* | ||
2 | * Fitipower FC0011 tuner driver | ||
3 | * | ||
4 | * Copyright (C) 2012 Michael Buesch <m@bues.ch> | ||
5 | * | ||
6 | * Derived from FC0012 tuner driver: | ||
7 | * Copyright (C) 2012 Hans-Frieder Vogt <hfvogt@gmx.net> | ||
8 | * | ||
9 | * This program is free software; you can redistribute it and/or modify | ||
10 | * it under the terms of the GNU General Public License as published by | ||
11 | * the Free Software Foundation; either version 2 of the License, or | ||
12 | * (at your option) any later version. | ||
13 | * | ||
14 | * This program is distributed in the hope that it will be useful, | ||
15 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
16 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
17 | * GNU General Public License for more details. | ||
18 | * | ||
19 | * You should have received a copy of the GNU General Public License | ||
20 | * along with this program; if not, write to the Free Software | ||
21 | * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. | ||
22 | */ | ||
23 | |||
24 | #include "fc0011.h" | ||
25 | |||
26 | |||
27 | /* Tuner registers */ | ||
28 | enum { | ||
29 | FC11_REG_0, | ||
30 | FC11_REG_FA, /* FA */ | ||
31 | FC11_REG_FP, /* FP */ | ||
32 | FC11_REG_XINHI, /* XIN high 8 bit */ | ||
33 | FC11_REG_XINLO, /* XIN low 8 bit */ | ||
34 | FC11_REG_VCO, /* VCO */ | ||
35 | FC11_REG_VCOSEL, /* VCO select */ | ||
36 | FC11_REG_7, /* Unknown tuner reg 7 */ | ||
37 | FC11_REG_8, /* Unknown tuner reg 8 */ | ||
38 | FC11_REG_9, | ||
39 | FC11_REG_10, /* Unknown tuner reg 10 */ | ||
40 | FC11_REG_11, /* Unknown tuner reg 11 */ | ||
41 | FC11_REG_12, | ||
42 | FC11_REG_RCCAL, /* RC calibrate */ | ||
43 | FC11_REG_VCOCAL, /* VCO calibrate */ | ||
44 | FC11_REG_15, | ||
45 | FC11_REG_16, /* Unknown tuner reg 16 */ | ||
46 | FC11_REG_17, | ||
47 | |||
48 | FC11_NR_REGS, /* Number of registers */ | ||
49 | }; | ||
50 | |||
51 | enum FC11_REG_VCOSEL_bits { | ||
52 | FC11_VCOSEL_2 = 0x08, /* VCO select 2 */ | ||
53 | FC11_VCOSEL_1 = 0x10, /* VCO select 1 */ | ||
54 | FC11_VCOSEL_CLKOUT = 0x20, /* Fix clock out */ | ||
55 | FC11_VCOSEL_BW7M = 0x40, /* 7MHz bw */ | ||
56 | FC11_VCOSEL_BW6M = 0x80, /* 6MHz bw */ | ||
57 | }; | ||
58 | |||
59 | enum FC11_REG_RCCAL_bits { | ||
60 | FC11_RCCAL_FORCE = 0x10, /* force */ | ||
61 | }; | ||
62 | |||
63 | enum FC11_REG_VCOCAL_bits { | ||
64 | FC11_VCOCAL_RUN = 0, /* VCO calibration run */ | ||
65 | FC11_VCOCAL_VALUEMASK = 0x3F, /* VCO calibration value mask */ | ||
66 | FC11_VCOCAL_OK = 0x40, /* VCO calibration Ok */ | ||
67 | FC11_VCOCAL_RESET = 0x80, /* VCO calibration reset */ | ||
68 | }; | ||
69 | |||
70 | |||
71 | struct fc0011_priv { | ||
72 | struct i2c_adapter *i2c; | ||
73 | u8 addr; | ||
74 | |||
75 | u32 frequency; | ||
76 | u32 bandwidth; | ||
77 | }; | ||
78 | |||
79 | |||
80 | static int fc0011_writereg(struct fc0011_priv *priv, u8 reg, u8 val) | ||
81 | { | ||
82 | u8 buf[2] = { reg, val }; | ||
83 | struct i2c_msg msg = { .addr = priv->addr, | ||
84 | .flags = 0, .buf = buf, .len = 2 }; | ||
85 | |||
86 | if (i2c_transfer(priv->i2c, &msg, 1) != 1) { | ||
87 | dev_err(&priv->i2c->dev, | ||
88 | "I2C write reg failed, reg: %02x, val: %02x\n", | ||
89 | reg, val); | ||
90 | return -EIO; | ||
91 | } | ||
92 | |||
93 | return 0; | ||
94 | } | ||
95 | |||
96 | static int fc0011_readreg(struct fc0011_priv *priv, u8 reg, u8 *val) | ||
97 | { | ||
98 | u8 dummy; | ||
99 | struct i2c_msg msg[2] = { | ||
100 | { .addr = priv->addr, | ||
101 | .flags = 0, .buf = ®, .len = 1 }, | ||
102 | { .addr = priv->addr, | ||
103 | .flags = I2C_M_RD, .buf = val ? : &dummy, .len = 1 }, | ||
104 | }; | ||
105 | |||
106 | if (i2c_transfer(priv->i2c, msg, 2) != 2) { | ||
107 | dev_err(&priv->i2c->dev, | ||
108 | "I2C read failed, reg: %02x\n", reg); | ||
109 | return -EIO; | ||
110 | } | ||
111 | |||
112 | return 0; | ||
113 | } | ||
114 | |||
115 | static int fc0011_release(struct dvb_frontend *fe) | ||
116 | { | ||
117 | kfree(fe->tuner_priv); | ||
118 | fe->tuner_priv = NULL; | ||
119 | |||
120 | return 0; | ||
121 | } | ||
122 | |||
123 | static int fc0011_init(struct dvb_frontend *fe) | ||
124 | { | ||
125 | struct fc0011_priv *priv = fe->tuner_priv; | ||
126 | int err; | ||
127 | |||
128 | if (WARN_ON(!fe->callback)) | ||
129 | return -EINVAL; | ||
130 | |||
131 | err = fe->callback(priv->i2c, DVB_FRONTEND_COMPONENT_TUNER, | ||
132 | FC0011_FE_CALLBACK_POWER, priv->addr); | ||
133 | if (err) { | ||
134 | dev_err(&priv->i2c->dev, "Power-on callback failed\n"); | ||
135 | return err; | ||
136 | } | ||
137 | err = fe->callback(priv->i2c, DVB_FRONTEND_COMPONENT_TUNER, | ||
138 | FC0011_FE_CALLBACK_RESET, priv->addr); | ||
139 | if (err) { | ||
140 | dev_err(&priv->i2c->dev, "Reset callback failed\n"); | ||
141 | return err; | ||
142 | } | ||
143 | |||
144 | return 0; | ||
145 | } | ||
146 | |||
147 | /* Initiate VCO calibration */ | ||
148 | static int fc0011_vcocal_trigger(struct fc0011_priv *priv) | ||
149 | { | ||
150 | int err; | ||
151 | |||
152 | err = fc0011_writereg(priv, FC11_REG_VCOCAL, FC11_VCOCAL_RESET); | ||
153 | if (err) | ||
154 | return err; | ||
155 | err = fc0011_writereg(priv, FC11_REG_VCOCAL, FC11_VCOCAL_RUN); | ||
156 | if (err) | ||
157 | return err; | ||
158 | |||
159 | return 0; | ||
160 | } | ||
161 | |||
162 | /* Read VCO calibration value */ | ||
163 | static int fc0011_vcocal_read(struct fc0011_priv *priv, u8 *value) | ||
164 | { | ||
165 | int err; | ||
166 | |||
167 | err = fc0011_writereg(priv, FC11_REG_VCOCAL, FC11_VCOCAL_RUN); | ||
168 | if (err) | ||
169 | return err; | ||
170 | msleep(10); | ||
171 | err = fc0011_readreg(priv, FC11_REG_VCOCAL, value); | ||
172 | if (err) | ||
173 | return err; | ||
174 | |||
175 | return 0; | ||
176 | } | ||
177 | |||
178 | static int fc0011_set_params(struct dvb_frontend *fe) | ||
179 | { | ||
180 | struct dtv_frontend_properties *p = &fe->dtv_property_cache; | ||
181 | struct fc0011_priv *priv = fe->tuner_priv; | ||
182 | int err; | ||
183 | unsigned int i, vco_retries; | ||
184 | u32 freq = p->frequency / 1000; | ||
185 | u32 bandwidth = p->bandwidth_hz / 1000; | ||
186 | u32 fvco, xin, xdiv, xdivr; | ||
187 | u16 frac; | ||
188 | u8 fa, fp, vco_sel, vco_cal; | ||
189 | u8 regs[FC11_NR_REGS] = { }; | ||
190 | |||
191 | regs[FC11_REG_7] = 0x0F; | ||
192 | regs[FC11_REG_8] = 0x3E; | ||
193 | regs[FC11_REG_10] = 0xB8; | ||
194 | regs[FC11_REG_11] = 0x80; | ||
195 | regs[FC11_REG_RCCAL] = 0x04; | ||
196 | err = fc0011_writereg(priv, FC11_REG_7, regs[FC11_REG_7]); | ||
197 | err |= fc0011_writereg(priv, FC11_REG_8, regs[FC11_REG_8]); | ||
198 | err |= fc0011_writereg(priv, FC11_REG_10, regs[FC11_REG_10]); | ||
199 | err |= fc0011_writereg(priv, FC11_REG_11, regs[FC11_REG_11]); | ||
200 | err |= fc0011_writereg(priv, FC11_REG_RCCAL, regs[FC11_REG_RCCAL]); | ||
201 | if (err) | ||
202 | return -EIO; | ||
203 | |||
204 | /* Set VCO freq and VCO div */ | ||
205 | if (freq < 54000) { | ||
206 | fvco = freq * 64; | ||
207 | regs[FC11_REG_VCO] = 0x82; | ||
208 | } else if (freq < 108000) { | ||
209 | fvco = freq * 32; | ||
210 | regs[FC11_REG_VCO] = 0x42; | ||
211 | } else if (freq < 216000) { | ||
212 | fvco = freq * 16; | ||
213 | regs[FC11_REG_VCO] = 0x22; | ||
214 | } else if (freq < 432000) { | ||
215 | fvco = freq * 8; | ||
216 | regs[FC11_REG_VCO] = 0x12; | ||
217 | } else { | ||
218 | fvco = freq * 4; | ||
219 | regs[FC11_REG_VCO] = 0x0A; | ||
220 | } | ||
221 | |||
222 | /* Calc XIN. The PLL reference frequency is 18 MHz. */ | ||
223 | xdiv = fvco / 18000; | ||
224 | frac = fvco - xdiv * 18000; | ||
225 | frac = (frac << 15) / 18000; | ||
226 | if (frac >= 16384) | ||
227 | frac += 32786; | ||
228 | if (!frac) | ||
229 | xin = 0; | ||
230 | else if (frac < 511) | ||
231 | xin = 512; | ||
232 | else if (frac < 65026) | ||
233 | xin = frac; | ||
234 | else | ||
235 | xin = 65024; | ||
236 | regs[FC11_REG_XINHI] = xin >> 8; | ||
237 | regs[FC11_REG_XINLO] = xin; | ||
238 | |||
239 | /* Calc FP and FA */ | ||
240 | xdivr = xdiv; | ||
241 | if (fvco - xdiv * 18000 >= 9000) | ||
242 | xdivr += 1; /* round */ | ||
243 | fp = xdivr / 8; | ||
244 | fa = xdivr - fp * 8; | ||
245 | if (fa < 2) { | ||
246 | fp -= 1; | ||
247 | fa += 8; | ||
248 | } | ||
249 | if (fp > 0x1F) { | ||
250 | fp &= 0x1F; | ||
251 | fa &= 0xF; | ||
252 | } | ||
253 | if (fa >= fp) { | ||
254 | dev_warn(&priv->i2c->dev, | ||
255 | "fa %02X >= fp %02X, but trying to continue\n", | ||
256 | (unsigned int)(u8)fa, (unsigned int)(u8)fp); | ||
257 | } | ||
258 | regs[FC11_REG_FA] = fa; | ||
259 | regs[FC11_REG_FP] = fp; | ||
260 | |||
261 | /* Select bandwidth */ | ||
262 | switch (bandwidth) { | ||
263 | case 8000: | ||
264 | break; | ||
265 | case 7000: | ||
266 | regs[FC11_REG_VCOSEL] |= FC11_VCOSEL_BW7M; | ||
267 | break; | ||
268 | default: | ||
269 | dev_warn(&priv->i2c->dev, "Unsupported bandwidth %u kHz. " | ||
270 | "Using 6000 kHz.\n", | ||
271 | bandwidth); | ||
272 | bandwidth = 6000; | ||
273 | /* fallthrough */ | ||
274 | case 6000: | ||
275 | regs[FC11_REG_VCOSEL] |= FC11_VCOSEL_BW6M; | ||
276 | break; | ||
277 | } | ||
278 | |||
279 | /* Pre VCO select */ | ||
280 | if (fvco < 2320000) { | ||
281 | vco_sel = 0; | ||
282 | regs[FC11_REG_VCOSEL] &= ~(FC11_VCOSEL_1 | FC11_VCOSEL_2); | ||
283 | } else if (fvco < 3080000) { | ||
284 | vco_sel = 1; | ||
285 | regs[FC11_REG_VCOSEL] &= ~(FC11_VCOSEL_1 | FC11_VCOSEL_2); | ||
286 | regs[FC11_REG_VCOSEL] |= FC11_VCOSEL_1; | ||
287 | } else { | ||
288 | vco_sel = 2; | ||
289 | regs[FC11_REG_VCOSEL] &= ~(FC11_VCOSEL_1 | FC11_VCOSEL_2); | ||
290 | regs[FC11_REG_VCOSEL] |= FC11_VCOSEL_2; | ||
291 | } | ||
292 | |||
293 | /* Fix for low freqs */ | ||
294 | if (freq < 45000) { | ||
295 | regs[FC11_REG_FA] = 0x6; | ||
296 | regs[FC11_REG_FP] = 0x11; | ||
297 | } | ||
298 | |||
299 | /* Clock out fix */ | ||
300 | regs[FC11_REG_VCOSEL] |= FC11_VCOSEL_CLKOUT; | ||
301 | |||
302 | /* Write the cached registers */ | ||
303 | for (i = FC11_REG_FA; i <= FC11_REG_VCOSEL; i++) { | ||
304 | err = fc0011_writereg(priv, i, regs[i]); | ||
305 | if (err) | ||
306 | return err; | ||
307 | } | ||
308 | |||
309 | /* VCO calibration */ | ||
310 | err = fc0011_vcocal_trigger(priv); | ||
311 | if (err) | ||
312 | return err; | ||
313 | err = fc0011_vcocal_read(priv, &vco_cal); | ||
314 | if (err) | ||
315 | return err; | ||
316 | vco_retries = 0; | ||
317 | while (!(vco_cal & FC11_VCOCAL_OK) && vco_retries < 6) { | ||
318 | /* Reset the tuner and try again */ | ||
319 | err = fe->callback(priv->i2c, DVB_FRONTEND_COMPONENT_TUNER, | ||
320 | FC0011_FE_CALLBACK_RESET, priv->addr); | ||
321 | if (err) { | ||
322 | dev_err(&priv->i2c->dev, "Failed to reset tuner\n"); | ||
323 | return err; | ||
324 | } | ||
325 | /* Reinit tuner config */ | ||
326 | err = 0; | ||
327 | for (i = FC11_REG_FA; i <= FC11_REG_VCOSEL; i++) | ||
328 | err |= fc0011_writereg(priv, i, regs[i]); | ||
329 | err |= fc0011_writereg(priv, FC11_REG_7, regs[FC11_REG_7]); | ||
330 | err |= fc0011_writereg(priv, FC11_REG_8, regs[FC11_REG_8]); | ||
331 | err |= fc0011_writereg(priv, FC11_REG_10, regs[FC11_REG_10]); | ||
332 | err |= fc0011_writereg(priv, FC11_REG_11, regs[FC11_REG_11]); | ||
333 | err |= fc0011_writereg(priv, FC11_REG_RCCAL, regs[FC11_REG_RCCAL]); | ||
334 | if (err) | ||
335 | return -EIO; | ||
336 | /* VCO calibration */ | ||
337 | err = fc0011_vcocal_trigger(priv); | ||
338 | if (err) | ||
339 | return err; | ||
340 | err = fc0011_vcocal_read(priv, &vco_cal); | ||
341 | if (err) | ||
342 | return err; | ||
343 | vco_retries++; | ||
344 | } | ||
345 | if (!(vco_cal & FC11_VCOCAL_OK)) { | ||
346 | dev_err(&priv->i2c->dev, | ||
347 | "Failed to read VCO calibration value (got %02X)\n", | ||
348 | (unsigned int)vco_cal); | ||
349 | return -EIO; | ||
350 | } | ||
351 | vco_cal &= FC11_VCOCAL_VALUEMASK; | ||
352 | |||
353 | switch (vco_sel) { | ||
354 | case 0: | ||
355 | if (vco_cal < 8) { | ||
356 | regs[FC11_REG_VCOSEL] &= ~(FC11_VCOSEL_1 | FC11_VCOSEL_2); | ||
357 | regs[FC11_REG_VCOSEL] |= FC11_VCOSEL_1; | ||
358 | err = fc0011_writereg(priv, FC11_REG_VCOSEL, | ||
359 | regs[FC11_REG_VCOSEL]); | ||
360 | if (err) | ||
361 | return err; | ||
362 | err = fc0011_vcocal_trigger(priv); | ||
363 | if (err) | ||
364 | return err; | ||
365 | } else { | ||
366 | regs[FC11_REG_VCOSEL] &= ~(FC11_VCOSEL_1 | FC11_VCOSEL_2); | ||
367 | err = fc0011_writereg(priv, FC11_REG_VCOSEL, | ||
368 | regs[FC11_REG_VCOSEL]); | ||
369 | if (err) | ||
370 | return err; | ||
371 | } | ||
372 | break; | ||
373 | case 1: | ||
374 | if (vco_cal < 5) { | ||
375 | regs[FC11_REG_VCOSEL] &= ~(FC11_VCOSEL_1 | FC11_VCOSEL_2); | ||
376 | regs[FC11_REG_VCOSEL] |= FC11_VCOSEL_2; | ||
377 | err = fc0011_writereg(priv, FC11_REG_VCOSEL, | ||
378 | regs[FC11_REG_VCOSEL]); | ||
379 | if (err) | ||
380 | return err; | ||
381 | err = fc0011_vcocal_trigger(priv); | ||
382 | if (err) | ||
383 | return err; | ||
384 | } else if (vco_cal <= 48) { | ||
385 | regs[FC11_REG_VCOSEL] &= ~(FC11_VCOSEL_1 | FC11_VCOSEL_2); | ||
386 | regs[FC11_REG_VCOSEL] |= FC11_VCOSEL_1; | ||
387 | err = fc0011_writereg(priv, FC11_REG_VCOSEL, | ||
388 | regs[FC11_REG_VCOSEL]); | ||
389 | if (err) | ||
390 | return err; | ||
391 | } else { | ||
392 | regs[FC11_REG_VCOSEL] &= ~(FC11_VCOSEL_1 | FC11_VCOSEL_2); | ||
393 | err = fc0011_writereg(priv, FC11_REG_VCOSEL, | ||
394 | regs[FC11_REG_VCOSEL]); | ||
395 | if (err) | ||
396 | return err; | ||
397 | err = fc0011_vcocal_trigger(priv); | ||
398 | if (err) | ||
399 | return err; | ||
400 | } | ||
401 | break; | ||
402 | case 2: | ||
403 | if (vco_cal > 53) { | ||
404 | regs[FC11_REG_VCOSEL] &= ~(FC11_VCOSEL_1 | FC11_VCOSEL_2); | ||
405 | regs[FC11_REG_VCOSEL] |= FC11_VCOSEL_1; | ||
406 | err = fc0011_writereg(priv, FC11_REG_VCOSEL, | ||
407 | regs[FC11_REG_VCOSEL]); | ||
408 | if (err) | ||
409 | return err; | ||
410 | err = fc0011_vcocal_trigger(priv); | ||
411 | if (err) | ||
412 | return err; | ||
413 | } else { | ||
414 | regs[FC11_REG_VCOSEL] &= ~(FC11_VCOSEL_1 | FC11_VCOSEL_2); | ||
415 | regs[FC11_REG_VCOSEL] |= FC11_VCOSEL_2; | ||
416 | err = fc0011_writereg(priv, FC11_REG_VCOSEL, | ||
417 | regs[FC11_REG_VCOSEL]); | ||
418 | if (err) | ||
419 | return err; | ||
420 | } | ||
421 | break; | ||
422 | } | ||
423 | err = fc0011_vcocal_read(priv, NULL); | ||
424 | if (err) | ||
425 | return err; | ||
426 | msleep(10); | ||
427 | |||
428 | err = fc0011_readreg(priv, FC11_REG_RCCAL, ®s[FC11_REG_RCCAL]); | ||
429 | if (err) | ||
430 | return err; | ||
431 | regs[FC11_REG_RCCAL] |= FC11_RCCAL_FORCE; | ||
432 | err = fc0011_writereg(priv, FC11_REG_RCCAL, regs[FC11_REG_RCCAL]); | ||
433 | if (err) | ||
434 | return err; | ||
435 | err = fc0011_writereg(priv, FC11_REG_16, 0xB); | ||
436 | if (err) | ||
437 | return err; | ||
438 | |||
439 | dev_dbg(&priv->i2c->dev, "Tuned to " | ||
440 | "fa=%02X fp=%02X xin=%02X%02X vco=%02X vcosel=%02X " | ||
441 | "vcocal=%02X(%u) bw=%u\n", | ||
442 | (unsigned int)regs[FC11_REG_FA], | ||
443 | (unsigned int)regs[FC11_REG_FP], | ||
444 | (unsigned int)regs[FC11_REG_XINHI], | ||
445 | (unsigned int)regs[FC11_REG_XINLO], | ||
446 | (unsigned int)regs[FC11_REG_VCO], | ||
447 | (unsigned int)regs[FC11_REG_VCOSEL], | ||
448 | (unsigned int)vco_cal, vco_retries, | ||
449 | (unsigned int)bandwidth); | ||
450 | |||
451 | priv->frequency = p->frequency; | ||
452 | priv->bandwidth = p->bandwidth_hz; | ||
453 | |||
454 | return 0; | ||
455 | } | ||
456 | |||
457 | static int fc0011_get_frequency(struct dvb_frontend *fe, u32 *frequency) | ||
458 | { | ||
459 | struct fc0011_priv *priv = fe->tuner_priv; | ||
460 | |||
461 | *frequency = priv->frequency; | ||
462 | |||
463 | return 0; | ||
464 | } | ||
465 | |||
466 | static int fc0011_get_if_frequency(struct dvb_frontend *fe, u32 *frequency) | ||
467 | { | ||
468 | *frequency = 0; | ||
469 | |||
470 | return 0; | ||
471 | } | ||
472 | |||
473 | static int fc0011_get_bandwidth(struct dvb_frontend *fe, u32 *bandwidth) | ||
474 | { | ||
475 | struct fc0011_priv *priv = fe->tuner_priv; | ||
476 | |||
477 | *bandwidth = priv->bandwidth; | ||
478 | |||
479 | return 0; | ||
480 | } | ||
481 | |||
482 | static const struct dvb_tuner_ops fc0011_tuner_ops = { | ||
483 | .info = { | ||
484 | .name = "Fitipower FC0011", | ||
485 | |||
486 | .frequency_min = 45000000, | ||
487 | .frequency_max = 1000000000, | ||
488 | }, | ||
489 | |||
490 | .release = fc0011_release, | ||
491 | .init = fc0011_init, | ||
492 | |||
493 | .set_params = fc0011_set_params, | ||
494 | |||
495 | .get_frequency = fc0011_get_frequency, | ||
496 | .get_if_frequency = fc0011_get_if_frequency, | ||
497 | .get_bandwidth = fc0011_get_bandwidth, | ||
498 | }; | ||
499 | |||
500 | struct dvb_frontend *fc0011_attach(struct dvb_frontend *fe, | ||
501 | struct i2c_adapter *i2c, | ||
502 | const struct fc0011_config *config) | ||
503 | { | ||
504 | struct fc0011_priv *priv; | ||
505 | |||
506 | priv = kzalloc(sizeof(struct fc0011_priv), GFP_KERNEL); | ||
507 | if (!priv) | ||
508 | return NULL; | ||
509 | |||
510 | priv->i2c = i2c; | ||
511 | priv->addr = config->i2c_address; | ||
512 | |||
513 | fe->tuner_priv = priv; | ||
514 | fe->ops.tuner_ops = fc0011_tuner_ops; | ||
515 | |||
516 | dev_info(&priv->i2c->dev, "Fitipower FC0011 tuner attached\n"); | ||
517 | |||
518 | return fe; | ||
519 | } | ||
520 | EXPORT_SYMBOL(fc0011_attach); | ||
521 | |||
522 | MODULE_DESCRIPTION("Fitipower FC0011 silicon tuner driver"); | ||
523 | MODULE_AUTHOR("Michael Buesch <m@bues.ch>"); | ||
524 | MODULE_LICENSE("GPL"); | ||
diff --git a/drivers/media/common/tuners/fc0011.h b/drivers/media/common/tuners/fc0011.h new file mode 100644 index 000000000000..0ee581f122d2 --- /dev/null +++ b/drivers/media/common/tuners/fc0011.h | |||
@@ -0,0 +1,41 @@ | |||
1 | #ifndef LINUX_FC0011_H_ | ||
2 | #define LINUX_FC0011_H_ | ||
3 | |||
4 | #include "dvb_frontend.h" | ||
5 | |||
6 | |||
7 | /** struct fc0011_config - fc0011 hardware config | ||
8 | * | ||
9 | * @i2c_address: I2C bus address. | ||
10 | */ | ||
11 | struct fc0011_config { | ||
12 | u8 i2c_address; | ||
13 | }; | ||
14 | |||
15 | /** enum fc0011_fe_callback_commands - Frontend callbacks | ||
16 | * | ||
17 | * @FC0011_FE_CALLBACK_POWER: Power on tuner hardware. | ||
18 | * @FC0011_FE_CALLBACK_RESET: Request a tuner reset. | ||
19 | */ | ||
20 | enum fc0011_fe_callback_commands { | ||
21 | FC0011_FE_CALLBACK_POWER, | ||
22 | FC0011_FE_CALLBACK_RESET, | ||
23 | }; | ||
24 | |||
25 | #if defined(CONFIG_MEDIA_TUNER_FC0011) ||\ | ||
26 | defined(CONFIG_MEDIA_TUNER_FC0011_MODULE) | ||
27 | struct dvb_frontend *fc0011_attach(struct dvb_frontend *fe, | ||
28 | struct i2c_adapter *i2c, | ||
29 | const struct fc0011_config *config); | ||
30 | #else | ||
31 | static inline | ||
32 | struct dvb_frontend *fc0011_attach(struct dvb_frontend *fe, | ||
33 | struct i2c_adapter *i2c, | ||
34 | const struct fc0011_config *config) | ||
35 | { | ||
36 | dev_err(&i2c->dev, "fc0011 driver disabled in Kconfig\n"); | ||
37 | return NULL; | ||
38 | } | ||
39 | #endif | ||
40 | |||
41 | #endif /* LINUX_FC0011_H_ */ | ||