diff options
author | Mauro Carvalho Chehab <mchehab@redhat.com> | 2013-04-05 13:35:18 -0400 |
---|---|---|
committer | Mauro Carvalho Chehab <mchehab@redhat.com> | 2013-04-16 20:18:42 -0400 |
commit | a80abc58feda48f868d748bde8c88592c2892b1d (patch) | |
tree | 69f6babbe159e0d0da96ad363553e407496c190c /drivers/media/tuners | |
parent | 098af4bde09792314ff106f545c405ffc5942c25 (diff) |
[media] r820t: Add a tuner driver for Rafael Micro R820T silicon tuner
This driver was written from scratch, based on an existing driver
that it is part of rtl-sdr git tree, released under GPLv2:
https://groups.google.com/forum/#!topic/ultra-cheap-sdr/Y3rBEOFtHug
https://github.com/n1gp/gr-baz
http://cgit.osmocom.org/rtl-sdr/plain/src/tuner_r820t.c
(there are also other variants of it out there)
>From what I understood from the threads, the original driver was converted
to userspace from a Realtek tree. I couldn't find the original tree.
However, the original driver look awkward on my eyes. So, I decided to
write a new version from it from the scratch, while trying to reproduce
everything found there.
TODO:
- After locking, the original driver seems to have some routines to
improve reception. This was not implemented here yet.
- RF Gain set/get is not implemented.
Signed-off-by: Mauro Carvalho Chehab <mchehab@redhat.com>
Tested-by: Antti Palosaari <crope@iki.fi>
Diffstat (limited to 'drivers/media/tuners')
-rw-r--r-- | drivers/media/tuners/Kconfig | 7 | ||||
-rw-r--r-- | drivers/media/tuners/Makefile | 1 | ||||
-rw-r--r-- | drivers/media/tuners/r820t.c | 1486 | ||||
-rw-r--r-- | drivers/media/tuners/r820t.h | 55 |
4 files changed, 1549 insertions, 0 deletions
diff --git a/drivers/media/tuners/Kconfig b/drivers/media/tuners/Kconfig index ffabd66dd14d..f6768cad001a 100644 --- a/drivers/media/tuners/Kconfig +++ b/drivers/media/tuners/Kconfig | |||
@@ -248,4 +248,11 @@ config MEDIA_TUNER_IT913X | |||
248 | default m if !MEDIA_SUBDRV_AUTOSELECT | 248 | default m if !MEDIA_SUBDRV_AUTOSELECT |
249 | help | 249 | help |
250 | ITE Tech IT913x silicon tuner driver. | 250 | ITE Tech IT913x silicon tuner driver. |
251 | |||
252 | config MEDIA_TUNER_R820T | ||
253 | tristate "Rafael Micro R820T silicon tuner" | ||
254 | depends on MEDIA_SUPPORT && I2C | ||
255 | default m if !MEDIA_SUBDRV_AUTOSELECT | ||
256 | help | ||
257 | Rafael Micro R820T silicon tuner driver. | ||
251 | endmenu | 258 | endmenu |
diff --git a/drivers/media/tuners/Makefile b/drivers/media/tuners/Makefile index 2ebe4b725b51..308f108eadba 100644 --- a/drivers/media/tuners/Makefile +++ b/drivers/media/tuners/Makefile | |||
@@ -35,6 +35,7 @@ obj-$(CONFIG_MEDIA_TUNER_FC0011) += fc0011.o | |||
35 | obj-$(CONFIG_MEDIA_TUNER_FC0012) += fc0012.o | 35 | obj-$(CONFIG_MEDIA_TUNER_FC0012) += fc0012.o |
36 | obj-$(CONFIG_MEDIA_TUNER_FC0013) += fc0013.o | 36 | obj-$(CONFIG_MEDIA_TUNER_FC0013) += fc0013.o |
37 | obj-$(CONFIG_MEDIA_TUNER_IT913X) += tuner_it913x.o | 37 | obj-$(CONFIG_MEDIA_TUNER_IT913X) += tuner_it913x.o |
38 | obj-$(CONFIG_MEDIA_TUNER_R820T) += r820t.o | ||
38 | 39 | ||
39 | ccflags-y += -I$(srctree)/drivers/media/dvb-core | 40 | ccflags-y += -I$(srctree)/drivers/media/dvb-core |
40 | ccflags-y += -I$(srctree)/drivers/media/dvb-frontends | 41 | ccflags-y += -I$(srctree)/drivers/media/dvb-frontends |
diff --git a/drivers/media/tuners/r820t.c b/drivers/media/tuners/r820t.c new file mode 100644 index 000000000000..7e02920f385a --- /dev/null +++ b/drivers/media/tuners/r820t.c | |||
@@ -0,0 +1,1486 @@ | |||
1 | /* | ||
2 | * Rafael Micro R820T driver | ||
3 | * | ||
4 | * Copyright (C) 2013 Mauro Carvalho Chehab <mchehab@redhat.com> | ||
5 | * | ||
6 | * This driver was written from scratch, based on an existing driver | ||
7 | * that it is part of rtl-sdr git tree, released under GPLv2: | ||
8 | * https://groups.google.com/forum/#!topic/ultra-cheap-sdr/Y3rBEOFtHug | ||
9 | * https://github.com/n1gp/gr-baz | ||
10 | * | ||
11 | * From what I understood from the threads, the original driver was converted | ||
12 | * to userspace from a Realtek tree. I couldn't find the original tree. | ||
13 | * However, the original driver look awkward on my eyes. So, I decided to | ||
14 | * write a new version from it from the scratch, while trying to reproduce | ||
15 | * everything found there. | ||
16 | * | ||
17 | * TODO: | ||
18 | * After locking, the original driver seems to have some routines to | ||
19 | * improve reception. This was not implemented here yet. | ||
20 | * | ||
21 | * RF Gain set/get is not implemented. | ||
22 | * | ||
23 | * This program is free software; you can redistribute it and/or modify | ||
24 | * it under the terms of the GNU General Public License as published by | ||
25 | * the Free Software Foundation; either version 2 of the License, or | ||
26 | * (at your option) any later version. | ||
27 | * | ||
28 | * This program is distributed in the hope that it will be useful, | ||
29 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
30 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
31 | * GNU General Public License for more details. | ||
32 | * | ||
33 | */ | ||
34 | |||
35 | #include <linux/videodev2.h> | ||
36 | #include <linux/mutex.h> | ||
37 | #include <linux/slab.h> | ||
38 | #include "tuner-i2c.h" | ||
39 | #include <asm/div64.h> | ||
40 | #include "r820t.h" | ||
41 | |||
42 | /* | ||
43 | * FIXME: I think that there are only 32 registers, but better safe than | ||
44 | * sorry. After finishing the driver, we may review it. | ||
45 | */ | ||
46 | #define REG_SHADOW_START 5 | ||
47 | #define NUM_REGS 27 | ||
48 | |||
49 | #define VER_NUM 49 | ||
50 | |||
51 | static int debug; | ||
52 | module_param(debug, int, 0644); | ||
53 | MODULE_PARM_DESC(debug, "enable verbose debug messages"); | ||
54 | |||
55 | /* | ||
56 | * enums and structures | ||
57 | */ | ||
58 | |||
59 | enum xtal_cap_value { | ||
60 | XTAL_LOW_CAP_30P = 0, | ||
61 | XTAL_LOW_CAP_20P, | ||
62 | XTAL_LOW_CAP_10P, | ||
63 | XTAL_LOW_CAP_0P, | ||
64 | XTAL_HIGH_CAP_0P | ||
65 | }; | ||
66 | |||
67 | struct r820t_priv { | ||
68 | struct list_head hybrid_tuner_instance_list; | ||
69 | const struct r820t_config *cfg; | ||
70 | struct tuner_i2c_props i2c_props; | ||
71 | struct mutex lock; | ||
72 | |||
73 | u8 regs[NUM_REGS]; | ||
74 | u8 buf[NUM_REGS + 1]; | ||
75 | enum xtal_cap_value xtal_cap_sel; | ||
76 | u16 pll; /* kHz */ | ||
77 | u32 int_freq; | ||
78 | u8 fil_cal_code; | ||
79 | bool imr_done; | ||
80 | |||
81 | /* Store current mode */ | ||
82 | u32 delsys; | ||
83 | enum v4l2_tuner_type type; | ||
84 | v4l2_std_id std; | ||
85 | u32 bw; /* in MHz */ | ||
86 | |||
87 | bool has_lock; | ||
88 | }; | ||
89 | |||
90 | struct r820t_freq_range { | ||
91 | u32 freq; | ||
92 | u8 open_d; | ||
93 | u8 rf_mux_ploy; | ||
94 | u8 tf_c; | ||
95 | u8 xtal_cap20p; | ||
96 | u8 xtal_cap10p; | ||
97 | u8 xtal_cap0p; | ||
98 | u8 imr_mem; /* Not used, currently */ | ||
99 | }; | ||
100 | |||
101 | #define VCO_POWER_REF 0x02 | ||
102 | |||
103 | /* | ||
104 | * Static constants | ||
105 | */ | ||
106 | |||
107 | static LIST_HEAD(hybrid_tuner_instance_list); | ||
108 | static DEFINE_MUTEX(r820t_list_mutex); | ||
109 | |||
110 | /* Those initial values start from REG_SHADOW_START */ | ||
111 | static const u8 r820t_init_array[NUM_REGS] = { | ||
112 | 0x83, 0x32, 0x75, /* 05 to 07 */ | ||
113 | 0xc0, 0x40, 0xd6, 0x6c, /* 08 to 0b */ | ||
114 | 0xf5, 0x63, 0x75, 0x68, /* 0c to 0f */ | ||
115 | 0x6c, 0x83, 0x80, 0x00, /* 10 to 13 */ | ||
116 | 0x0f, 0x00, 0xc0, 0x30, /* 14 to 17 */ | ||
117 | 0x48, 0xcc, 0x60, 0x00, /* 18 to 1b */ | ||
118 | 0x54, 0xae, 0x4a, 0xc0 /* 1c to 1f */ | ||
119 | }; | ||
120 | |||
121 | /* Tuner frequency ranges */ | ||
122 | static const struct r820t_freq_range freq_ranges[] = { | ||
123 | { | ||
124 | .freq = 0, | ||
125 | .open_d = 0x08, /* low */ | ||
126 | .rf_mux_ploy = 0x02, /* R26[7:6]=0 (LPF) R26[1:0]=2 (low) */ | ||
127 | .tf_c = 0xdf, /* R27[7:0] band2,band0 */ | ||
128 | .xtal_cap20p = 0x02, /* R16[1:0] 20pF (10) */ | ||
129 | .xtal_cap10p = 0x01, | ||
130 | .xtal_cap0p = 0x00, | ||
131 | .imr_mem = 0, | ||
132 | }, { | ||
133 | .freq = 50, /* Start freq, in MHz */ | ||
134 | .open_d = 0x08, /* low */ | ||
135 | .rf_mux_ploy = 0x02, /* R26[7:6]=0 (LPF) R26[1:0]=2 (low) */ | ||
136 | .tf_c = 0xbe, /* R27[7:0] band4,band1 */ | ||
137 | .xtal_cap20p = 0x02, /* R16[1:0] 20pF (10) */ | ||
138 | .xtal_cap10p = 0x01, | ||
139 | .xtal_cap0p = 0x00, | ||
140 | .imr_mem = 0, | ||
141 | }, { | ||
142 | .freq = 55, /* Start freq, in MHz */ | ||
143 | .open_d = 0x08, /* low */ | ||
144 | .rf_mux_ploy = 0x02, /* R26[7:6]=0 (LPF) R26[1:0]=2 (low) */ | ||
145 | .tf_c = 0x8b, /* R27[7:0] band7,band4 */ | ||
146 | .xtal_cap20p = 0x02, /* R16[1:0] 20pF (10) */ | ||
147 | .xtal_cap10p = 0x01, | ||
148 | .xtal_cap0p = 0x00, | ||
149 | .imr_mem = 0, | ||
150 | }, { | ||
151 | .freq = 60, /* Start freq, in MHz */ | ||
152 | .open_d = 0x08, /* low */ | ||
153 | .rf_mux_ploy = 0x02, /* R26[7:6]=0 (LPF) R26[1:0]=2 (low) */ | ||
154 | .tf_c = 0x7b, /* R27[7:0] band8,band4 */ | ||
155 | .xtal_cap20p = 0x02, /* R16[1:0] 20pF (10) */ | ||
156 | .xtal_cap10p = 0x01, | ||
157 | .xtal_cap0p = 0x00, | ||
158 | .imr_mem = 0, | ||
159 | }, { | ||
160 | .freq = 65, /* Start freq, in MHz */ | ||
161 | .open_d = 0x08, /* low */ | ||
162 | .rf_mux_ploy = 0x02, /* R26[7:6]=0 (LPF) R26[1:0]=2 (low) */ | ||
163 | .tf_c = 0x69, /* R27[7:0] band9,band6 */ | ||
164 | .xtal_cap20p = 0x02, /* R16[1:0] 20pF (10) */ | ||
165 | .xtal_cap10p = 0x01, | ||
166 | .xtal_cap0p = 0x00, | ||
167 | .imr_mem = 0, | ||
168 | }, { | ||
169 | .freq = 70, /* Start freq, in MHz */ | ||
170 | .open_d = 0x08, /* low */ | ||
171 | .rf_mux_ploy = 0x02, /* R26[7:6]=0 (LPF) R26[1:0]=2 (low) */ | ||
172 | .tf_c = 0x58, /* R27[7:0] band10,band7 */ | ||
173 | .xtal_cap20p = 0x02, /* R16[1:0] 20pF (10) */ | ||
174 | .xtal_cap10p = 0x01, | ||
175 | .xtal_cap0p = 0x00, | ||
176 | .imr_mem = 0, | ||
177 | }, { | ||
178 | .freq = 75, /* Start freq, in MHz */ | ||
179 | .open_d = 0x00, /* high */ | ||
180 | .rf_mux_ploy = 0x02, /* R26[7:6]=0 (LPF) R26[1:0]=2 (low) */ | ||
181 | .tf_c = 0x44, /* R27[7:0] band11,band11 */ | ||
182 | .xtal_cap20p = 0x02, /* R16[1:0] 20pF (10) */ | ||
183 | .xtal_cap10p = 0x01, | ||
184 | .xtal_cap0p = 0x00, | ||
185 | .imr_mem = 0, | ||
186 | }, { | ||
187 | .freq = 80, /* Start freq, in MHz */ | ||
188 | .open_d = 0x00, /* high */ | ||
189 | .rf_mux_ploy = 0x02, /* R26[7:6]=0 (LPF) R26[1:0]=2 (low) */ | ||
190 | .tf_c = 0x44, /* R27[7:0] band11,band11 */ | ||
191 | .xtal_cap20p = 0x02, /* R16[1:0] 20pF (10) */ | ||
192 | .xtal_cap10p = 0x01, | ||
193 | .xtal_cap0p = 0x00, | ||
194 | .imr_mem = 0, | ||
195 | }, { | ||
196 | .freq = 90, /* Start freq, in MHz */ | ||
197 | .open_d = 0x00, /* high */ | ||
198 | .rf_mux_ploy = 0x02, /* R26[7:6]=0 (LPF) R26[1:0]=2 (low) */ | ||
199 | .tf_c = 0x34, /* R27[7:0] band12,band11 */ | ||
200 | .xtal_cap20p = 0x01, /* R16[1:0] 10pF (01) */ | ||
201 | .xtal_cap10p = 0x01, | ||
202 | .xtal_cap0p = 0x00, | ||
203 | .imr_mem = 0, | ||
204 | }, { | ||
205 | .freq = 100, /* Start freq, in MHz */ | ||
206 | .open_d = 0x00, /* high */ | ||
207 | .rf_mux_ploy = 0x02, /* R26[7:6]=0 (LPF) R26[1:0]=2 (low) */ | ||
208 | .tf_c = 0x34, /* R27[7:0] band12,band11 */ | ||
209 | .xtal_cap20p = 0x01, /* R16[1:0] 10pF (01) */ | ||
210 | .xtal_cap10p = 0x01, | ||
211 | .xtal_cap0p = 0x00, | ||
212 | .imr_mem = 0, | ||
213 | }, { | ||
214 | .freq = 110, /* Start freq, in MHz */ | ||
215 | .open_d = 0x00, /* high */ | ||
216 | .rf_mux_ploy = 0x02, /* R26[7:6]=0 (LPF) R26[1:0]=2 (low) */ | ||
217 | .tf_c = 0x24, /* R27[7:0] band13,band11 */ | ||
218 | .xtal_cap20p = 0x01, /* R16[1:0] 10pF (01) */ | ||
219 | .xtal_cap10p = 0x01, | ||
220 | .xtal_cap0p = 0x00, | ||
221 | .imr_mem = 1, | ||
222 | }, { | ||
223 | .freq = 120, /* Start freq, in MHz */ | ||
224 | .open_d = 0x00, /* high */ | ||
225 | .rf_mux_ploy = 0x02, /* R26[7:6]=0 (LPF) R26[1:0]=2 (low) */ | ||
226 | .tf_c = 0x24, /* R27[7:0] band13,band11 */ | ||
227 | .xtal_cap20p = 0x01, /* R16[1:0] 10pF (01) */ | ||
228 | .xtal_cap10p = 0x01, | ||
229 | .xtal_cap0p = 0x00, | ||
230 | .imr_mem = 1, | ||
231 | }, { | ||
232 | .freq = 140, /* Start freq, in MHz */ | ||
233 | .open_d = 0x00, /* high */ | ||
234 | .rf_mux_ploy = 0x02, /* R26[7:6]=0 (LPF) R26[1:0]=2 (low) */ | ||
235 | .tf_c = 0x14, /* R27[7:0] band14,band11 */ | ||
236 | .xtal_cap20p = 0x01, /* R16[1:0] 10pF (01) */ | ||
237 | .xtal_cap10p = 0x01, | ||
238 | .xtal_cap0p = 0x00, | ||
239 | .imr_mem = 1, | ||
240 | }, { | ||
241 | .freq = 180, /* Start freq, in MHz */ | ||
242 | .open_d = 0x00, /* high */ | ||
243 | .rf_mux_ploy = 0x02, /* R26[7:6]=0 (LPF) R26[1:0]=2 (low) */ | ||
244 | .tf_c = 0x13, /* R27[7:0] band14,band12 */ | ||
245 | .xtal_cap20p = 0x00, /* R16[1:0] 0pF (00) */ | ||
246 | .xtal_cap10p = 0x00, | ||
247 | .xtal_cap0p = 0x00, | ||
248 | .imr_mem = 1, | ||
249 | }, { | ||
250 | .freq = 220, /* Start freq, in MHz */ | ||
251 | .open_d = 0x00, /* high */ | ||
252 | .rf_mux_ploy = 0x02, /* R26[7:6]=0 (LPF) R26[1:0]=2 (low) */ | ||
253 | .tf_c = 0x13, /* R27[7:0] band14,band12 */ | ||
254 | .xtal_cap20p = 0x00, /* R16[1:0] 0pF (00) */ | ||
255 | .xtal_cap10p = 0x00, | ||
256 | .xtal_cap0p = 0x00, | ||
257 | .imr_mem = 2, | ||
258 | }, { | ||
259 | .freq = 250, /* Start freq, in MHz */ | ||
260 | .open_d = 0x00, /* high */ | ||
261 | .rf_mux_ploy = 0x02, /* R26[7:6]=0 (LPF) R26[1:0]=2 (low) */ | ||
262 | .tf_c = 0x11, /* R27[7:0] highest,highest */ | ||
263 | .xtal_cap20p = 0x00, /* R16[1:0] 0pF (00) */ | ||
264 | .xtal_cap10p = 0x00, | ||
265 | .xtal_cap0p = 0x00, | ||
266 | .imr_mem = 2, | ||
267 | }, { | ||
268 | .freq = 280, /* Start freq, in MHz */ | ||
269 | .open_d = 0x00, /* high */ | ||
270 | .rf_mux_ploy = 0x02, /* R26[7:6]=0 (LPF) R26[1:0]=2 (low) */ | ||
271 | .tf_c = 0x00, /* R27[7:0] highest,highest */ | ||
272 | .xtal_cap20p = 0x00, /* R16[1:0] 0pF (00) */ | ||
273 | .xtal_cap10p = 0x00, | ||
274 | .xtal_cap0p = 0x00, | ||
275 | .imr_mem = 2, | ||
276 | }, { | ||
277 | .freq = 310, /* Start freq, in MHz */ | ||
278 | .open_d = 0x00, /* high */ | ||
279 | .rf_mux_ploy = 0x41, /* R26[7:6]=1 (bypass) R26[1:0]=1 (middle) */ | ||
280 | .tf_c = 0x00, /* R27[7:0] highest,highest */ | ||
281 | .xtal_cap20p = 0x00, /* R16[1:0] 0pF (00) */ | ||
282 | .xtal_cap10p = 0x00, | ||
283 | .xtal_cap0p = 0x00, | ||
284 | .imr_mem = 2, | ||
285 | }, { | ||
286 | .freq = 450, /* Start freq, in MHz */ | ||
287 | .open_d = 0x00, /* high */ | ||
288 | .rf_mux_ploy = 0x41, /* R26[7:6]=1 (bypass) R26[1:0]=1 (middle) */ | ||
289 | .tf_c = 0x00, /* R27[7:0] highest,highest */ | ||
290 | .xtal_cap20p = 0x00, /* R16[1:0] 0pF (00) */ | ||
291 | .xtal_cap10p = 0x00, | ||
292 | .xtal_cap0p = 0x00, | ||
293 | .imr_mem = 3, | ||
294 | }, { | ||
295 | .freq = 588, /* Start freq, in MHz */ | ||
296 | .open_d = 0x00, /* high */ | ||
297 | .rf_mux_ploy = 0x40, /* R26[7:6]=1 (bypass) R26[1:0]=0 (highest) */ | ||
298 | .tf_c = 0x00, /* R27[7:0] highest,highest */ | ||
299 | .xtal_cap20p = 0x00, /* R16[1:0] 0pF (00) */ | ||
300 | .xtal_cap10p = 0x00, | ||
301 | .xtal_cap0p = 0x00, | ||
302 | .imr_mem = 3, | ||
303 | }, { | ||
304 | .freq = 650, /* Start freq, in MHz */ | ||
305 | .open_d = 0x00, /* high */ | ||
306 | .rf_mux_ploy = 0x40, /* R26[7:6]=1 (bypass) R26[1:0]=0 (highest) */ | ||
307 | .tf_c = 0x00, /* R27[7:0] highest,highest */ | ||
308 | .xtal_cap20p = 0x00, /* R16[1:0] 0pF (00) */ | ||
309 | .xtal_cap10p = 0x00, | ||
310 | .xtal_cap0p = 0x00, | ||
311 | .imr_mem = 4, | ||
312 | } | ||
313 | }; | ||
314 | |||
315 | static int r820t_xtal_capacitor[][2] = { | ||
316 | { 0x0b, XTAL_LOW_CAP_30P }, | ||
317 | { 0x02, XTAL_LOW_CAP_20P }, | ||
318 | { 0x01, XTAL_LOW_CAP_10P }, | ||
319 | { 0x00, XTAL_LOW_CAP_0P }, | ||
320 | { 0x10, XTAL_HIGH_CAP_0P }, | ||
321 | }; | ||
322 | |||
323 | /* | ||
324 | * I2C read/write code and shadow registers logic | ||
325 | */ | ||
326 | static void shadow_store(struct r820t_priv *priv, u8 reg, const u8 *val, | ||
327 | int len) | ||
328 | { | ||
329 | int r = reg - REG_SHADOW_START; | ||
330 | |||
331 | if (r < 0) { | ||
332 | len += r; | ||
333 | r = 0; | ||
334 | } | ||
335 | if (len <= 0) | ||
336 | return; | ||
337 | if (len > NUM_REGS) | ||
338 | len = NUM_REGS; | ||
339 | |||
340 | tuner_dbg("%s: prev reg=%02x len=%d: %*ph\n", | ||
341 | __func__, r + REG_SHADOW_START, len, len, val); | ||
342 | |||
343 | memcpy(&priv->regs[r], val, len); | ||
344 | } | ||
345 | |||
346 | static int r820t_write(struct r820t_priv *priv, u8 reg, const u8 *val, | ||
347 | int len) | ||
348 | { | ||
349 | int rc, size, pos = 0; | ||
350 | |||
351 | /* Store the shadow registers */ | ||
352 | shadow_store(priv, reg, val, len); | ||
353 | |||
354 | do { | ||
355 | if (len > priv->cfg->max_i2c_msg_len - 1) | ||
356 | size = priv->cfg->max_i2c_msg_len - 1; | ||
357 | else | ||
358 | size = len; | ||
359 | |||
360 | /* Fill I2C buffer */ | ||
361 | priv->buf[0] = reg; | ||
362 | memcpy(&priv->buf[1], &val[pos], size); | ||
363 | |||
364 | rc = tuner_i2c_xfer_send(&priv->i2c_props, priv->buf, size + 1); | ||
365 | if (rc != size + 1) { | ||
366 | tuner_info("%s: i2c wr failed=%d reg=%02x len=%d: %*ph\n", | ||
367 | __func__, rc, reg, size, size, &priv->buf[1]); | ||
368 | if (rc < 0) | ||
369 | return rc; | ||
370 | return -EREMOTEIO; | ||
371 | } | ||
372 | tuner_dbg("%s: i2c wr reg=%02x len=%d: %*ph\n", | ||
373 | __func__, reg, size, size, &priv->buf[1]); | ||
374 | |||
375 | reg += size; | ||
376 | len -= size; | ||
377 | pos += size; | ||
378 | } while (len > 0); | ||
379 | |||
380 | return 0; | ||
381 | } | ||
382 | |||
383 | static int r820t_write_reg(struct r820t_priv *priv, u8 reg, u8 val) | ||
384 | { | ||
385 | return r820t_write(priv, reg, &val, 1); | ||
386 | } | ||
387 | |||
388 | static int r820t_write_reg_mask(struct r820t_priv *priv, u8 reg, u8 val, | ||
389 | u8 bit_mask) | ||
390 | { | ||
391 | int r = reg - REG_SHADOW_START; | ||
392 | |||
393 | if (r >= 0 && r < NUM_REGS) | ||
394 | val = (priv->regs[r] & ~bit_mask) | (val & bit_mask); | ||
395 | else | ||
396 | return -EINVAL; | ||
397 | |||
398 | return r820t_write(priv, reg, &val, 1); | ||
399 | } | ||
400 | |||
401 | static int r820_read(struct r820t_priv *priv, u8 reg, u8 *val, int len) | ||
402 | { | ||
403 | int rc; | ||
404 | u8 *p = &priv->buf[1]; | ||
405 | |||
406 | priv->buf[0] = reg; | ||
407 | |||
408 | rc = tuner_i2c_xfer_send_recv(&priv->i2c_props, priv->buf, 1, p, len); | ||
409 | if (rc != len) { | ||
410 | tuner_info("%s: i2c rd failed=%d reg=%02x len=%d: %*ph\n", | ||
411 | __func__, rc, reg, len, len, p); | ||
412 | if (rc < 0) | ||
413 | return rc; | ||
414 | return -EREMOTEIO; | ||
415 | } | ||
416 | tuner_dbg("%s: i2c rd reg=%02x len=%d: %*ph\n", | ||
417 | __func__, reg, len, len, p); | ||
418 | |||
419 | /* Copy data to the output buffer */ | ||
420 | memcpy(val, p, len); | ||
421 | |||
422 | return 0; | ||
423 | } | ||
424 | |||
425 | /* | ||
426 | * r820t tuning logic | ||
427 | */ | ||
428 | |||
429 | static int r820t_set_mux(struct r820t_priv *priv, u32 freq) | ||
430 | { | ||
431 | const struct r820t_freq_range *range; | ||
432 | int i, rc; | ||
433 | u8 val; | ||
434 | |||
435 | /* Get the proper frequency range */ | ||
436 | freq = freq / 1000000; | ||
437 | for (i = 0; i < ARRAY_SIZE(freq_ranges) - 1; i++) { | ||
438 | if (freq < freq_ranges[i + 1].freq) | ||
439 | break; | ||
440 | } | ||
441 | range = &freq_ranges[i]; | ||
442 | |||
443 | tuner_dbg("set r820t range#%d for frequency %d MHz\n", i, freq); | ||
444 | |||
445 | /* Open Drain */ | ||
446 | rc = r820t_write_reg_mask(priv, 0x17, range->open_d, 0x08); | ||
447 | if (rc < 0) | ||
448 | return rc; | ||
449 | |||
450 | /* RF_MUX,Polymux */ | ||
451 | rc = r820t_write_reg_mask(priv, 0x1a, range->rf_mux_ploy, 0xc3); | ||
452 | if (rc < 0) | ||
453 | return rc; | ||
454 | |||
455 | /* TF BAND */ | ||
456 | rc = r820t_write_reg(priv, 0x1b, range->tf_c); | ||
457 | if (rc < 0) | ||
458 | return rc; | ||
459 | |||
460 | /* XTAL CAP & Drive */ | ||
461 | switch (priv->xtal_cap_sel) { | ||
462 | case XTAL_LOW_CAP_30P: | ||
463 | case XTAL_LOW_CAP_20P: | ||
464 | val = range->xtal_cap20p | 0x08; | ||
465 | break; | ||
466 | case XTAL_LOW_CAP_10P: | ||
467 | val = range->xtal_cap10p | 0x08; | ||
468 | break; | ||
469 | case XTAL_HIGH_CAP_0P: | ||
470 | val = range->xtal_cap0p | 0x00; | ||
471 | break; | ||
472 | default: | ||
473 | case XTAL_LOW_CAP_0P: | ||
474 | val = range->xtal_cap0p | 0x08; | ||
475 | break; | ||
476 | } | ||
477 | rc = r820t_write_reg_mask(priv, 0x10, val, 0x0b); | ||
478 | if (rc < 0) | ||
479 | return rc; | ||
480 | |||
481 | /* | ||
482 | * FIXME: the original driver has a logic there with preserves | ||
483 | * gain/phase from registers 8 and 9 reading the data from the | ||
484 | * registers before writing, if "IMF done". That code was sort of | ||
485 | * commented there, as the flag is always false. | ||
486 | */ | ||
487 | rc = r820t_write_reg_mask(priv, 0x08, 0, 0x3f); | ||
488 | if (rc < 0) | ||
489 | return rc; | ||
490 | |||
491 | rc = r820t_write_reg_mask(priv, 0x09, 0, 0x3f); | ||
492 | |||
493 | return rc; | ||
494 | } | ||
495 | |||
496 | static int r820t_set_pll(struct r820t_priv *priv, u32 freq) | ||
497 | { | ||
498 | u64 tmp64, vco_freq; | ||
499 | int rc, i; | ||
500 | u32 vco_fra; /* VCO contribution by SDM (kHz) */ | ||
501 | u32 vco_min = 1770000; | ||
502 | u32 vco_max = vco_min * 2; | ||
503 | u32 pll_ref; | ||
504 | u16 n_sdm = 2; | ||
505 | u16 sdm = 0; | ||
506 | u8 mix_div = 2; | ||
507 | u8 div_buf = 0; | ||
508 | u8 div_num = 0; | ||
509 | u8 ni, si, nint, vco_fine_tune, val; | ||
510 | u8 data[5]; | ||
511 | |||
512 | freq = freq / 1000; /* Frequency in kHz */ | ||
513 | |||
514 | pll_ref = priv->cfg->xtal / 1000; | ||
515 | |||
516 | tuner_dbg("set r820t pll for frequency %d kHz = %d\n", freq, pll_ref); | ||
517 | |||
518 | /* FIXME: this seems to be a hack - probably it can be removed */ | ||
519 | rc = r820t_write_reg_mask(priv, 0x10, 0x00, 0x00); | ||
520 | if (rc < 0) | ||
521 | return rc; | ||
522 | |||
523 | /* set pll autotune = 128kHz */ | ||
524 | rc = r820t_write_reg_mask(priv, 0x1a, 0x00, 0x0c); | ||
525 | if (rc < 0) | ||
526 | return rc; | ||
527 | |||
528 | /* set VCO current = 100 */ | ||
529 | rc = r820t_write_reg_mask(priv, 0x12, 0x80, 0xe0); | ||
530 | if (rc < 0) | ||
531 | return rc; | ||
532 | |||
533 | /* Calculate divider */ | ||
534 | while (mix_div <= 64) { | ||
535 | if (((freq * mix_div) >= vco_min) && | ||
536 | ((freq * mix_div) < vco_max)) { | ||
537 | div_buf = mix_div; | ||
538 | while (div_buf > 2) { | ||
539 | div_buf = div_buf >> 1; | ||
540 | div_num++; | ||
541 | } | ||
542 | break; | ||
543 | } | ||
544 | mix_div = mix_div << 1; | ||
545 | } | ||
546 | |||
547 | rc = r820_read(priv, 0x00, data, sizeof(data)); | ||
548 | if (rc < 0) | ||
549 | return rc; | ||
550 | |||
551 | vco_fine_tune = (data[4] & 0x30) >> 4; | ||
552 | |||
553 | if (vco_fine_tune > VCO_POWER_REF) | ||
554 | div_num = div_num - 1; | ||
555 | else if (vco_fine_tune < VCO_POWER_REF) | ||
556 | div_num = div_num + 1; | ||
557 | |||
558 | rc = r820t_write_reg_mask(priv, 0x10, div_num << 5, 0xe0); | ||
559 | if (rc < 0) | ||
560 | return rc; | ||
561 | |||
562 | vco_freq = (u64)(freq * (u64)mix_div); | ||
563 | |||
564 | tmp64 = vco_freq; | ||
565 | do_div(tmp64, 2 * pll_ref); | ||
566 | nint = (u8)tmp64; | ||
567 | |||
568 | tmp64 = vco_freq - ((u64)2) * pll_ref * nint; | ||
569 | do_div(tmp64, 1000); | ||
570 | vco_fra = (u16)(tmp64); | ||
571 | |||
572 | pll_ref /= 1000; | ||
573 | |||
574 | /* boundary spur prevention */ | ||
575 | if (vco_fra < pll_ref / 64) { | ||
576 | vco_fra = 0; | ||
577 | } else if (vco_fra > pll_ref * 127 / 64) { | ||
578 | vco_fra = 0; | ||
579 | nint++; | ||
580 | } else if ((vco_fra > pll_ref * 127 / 128) && (vco_fra < pll_ref)) { | ||
581 | vco_fra = pll_ref * 127 / 128; | ||
582 | } else if ((vco_fra > pll_ref) && (vco_fra < pll_ref * 129 / 128)) { | ||
583 | vco_fra = pll_ref * 129 / 128; | ||
584 | } | ||
585 | |||
586 | if (nint > 63) { | ||
587 | tuner_info("No valid PLL values for %u kHz!\n", freq); | ||
588 | return -EINVAL; | ||
589 | } | ||
590 | |||
591 | ni = (nint - 13) / 4; | ||
592 | si = nint - 4 * ni - 13; | ||
593 | |||
594 | rc = r820t_write_reg(priv, 0x14, ni + (si << 6)); | ||
595 | if (rc < 0) | ||
596 | return rc; | ||
597 | |||
598 | /* pw_sdm */ | ||
599 | if (!vco_fra) | ||
600 | val = 0x08; | ||
601 | else | ||
602 | val = 0x00; | ||
603 | |||
604 | rc = r820t_write_reg_mask(priv, 0x12, val, 0x08); | ||
605 | if (rc < 0) | ||
606 | return rc; | ||
607 | |||
608 | /* sdm calculator */ | ||
609 | while (vco_fra > 1) { | ||
610 | if (vco_fra > (2 * pll_ref / n_sdm)) { | ||
611 | sdm = sdm + 32768 / (n_sdm / 2); | ||
612 | vco_fra = vco_fra - 2 * pll_ref / n_sdm; | ||
613 | if (n_sdm >= 0x8000) | ||
614 | break; | ||
615 | } | ||
616 | n_sdm = n_sdm << 1; | ||
617 | } | ||
618 | |||
619 | rc = r820t_write_reg_mask(priv, 0x16, sdm >> 8, 0x08); | ||
620 | if (rc < 0) | ||
621 | return rc; | ||
622 | rc = r820t_write_reg_mask(priv, 0x15, sdm & 0xff, 0x08); | ||
623 | if (rc < 0) | ||
624 | return rc; | ||
625 | |||
626 | for (i = 0; i < 2; i++) { | ||
627 | /* | ||
628 | * FIXME: Rafael chips R620D, R828D and R828 seems to | ||
629 | * need 20 ms for analog TV | ||
630 | */ | ||
631 | msleep(10); | ||
632 | |||
633 | /* Check if PLL has locked */ | ||
634 | rc = r820_read(priv, 0x00, data, 3); | ||
635 | if (rc < 0) | ||
636 | return rc; | ||
637 | if (data[2] & 0x40) | ||
638 | break; | ||
639 | |||
640 | if (!i) { | ||
641 | /* Didn't lock. Increase VCO current */ | ||
642 | rc = r820t_write_reg_mask(priv, 0x12, 0x60, 0xe0); | ||
643 | if (rc < 0) | ||
644 | return rc; | ||
645 | } | ||
646 | } | ||
647 | |||
648 | if (!(data[2] & 0x40)) { | ||
649 | priv->has_lock = false; | ||
650 | return 0; | ||
651 | } | ||
652 | |||
653 | priv->has_lock = true; | ||
654 | tuner_dbg("tuner has lock at frequency %d kHz\n", freq); | ||
655 | |||
656 | /* set pll autotune = 8kHz */ | ||
657 | rc = r820t_write_reg_mask(priv, 0x1a, 0x08, 0x08); | ||
658 | |||
659 | return rc; | ||
660 | } | ||
661 | |||
662 | static int r820t_sysfreq_sel(struct r820t_priv *priv, u32 freq, | ||
663 | enum v4l2_tuner_type type, | ||
664 | v4l2_std_id std, | ||
665 | u32 delsys) | ||
666 | { | ||
667 | int rc; | ||
668 | u8 mixer_top, lna_top, cp_cur, div_buf_cur, lna_vth_l, mixer_vth_l; | ||
669 | u8 air_cable1_in, cable2_in, pre_dect, lna_discharge, filter_cur; | ||
670 | |||
671 | tuner_dbg("adjusting tuner parameters for the standard\n"); | ||
672 | |||
673 | switch (delsys) { | ||
674 | case SYS_DVBT: | ||
675 | if ((freq == 506000000) || (freq == 666000000) || | ||
676 | (freq == 818000000)) { | ||
677 | mixer_top = 0x14; /* mixer top:14 , top-1, low-discharge */ | ||
678 | lna_top = 0xe5; /* detect bw 3, lna top:4, predet top:2 */ | ||
679 | cp_cur = 0x28; /* 101, 0.2 */ | ||
680 | div_buf_cur = 0x20; /* 10, 200u */ | ||
681 | } else { | ||
682 | mixer_top = 0x24; /* mixer top:13 , top-1, low-discharge */ | ||
683 | lna_top = 0xe5; /* detect bw 3, lna top:4, predet top:2 */ | ||
684 | cp_cur = 0x38; /* 111, auto */ | ||
685 | div_buf_cur = 0x30; /* 11, 150u */ | ||
686 | } | ||
687 | lna_vth_l = 0x53; /* lna vth 0.84 , vtl 0.64 */ | ||
688 | mixer_vth_l = 0x75; /* mixer vth 1.04, vtl 0.84 */ | ||
689 | air_cable1_in = 0x00; | ||
690 | cable2_in = 0x00; | ||
691 | pre_dect = 0x40; | ||
692 | lna_discharge = 14; | ||
693 | filter_cur = 0x40; /* 10, low */ | ||
694 | break; | ||
695 | case SYS_DVBT2: | ||
696 | mixer_top = 0x24; /* mixer top:13 , top-1, low-discharge */ | ||
697 | lna_top = 0xe5; /* detect bw 3, lna top:4, predet top:2 */ | ||
698 | lna_vth_l = 0x53; /* lna vth 0.84 , vtl 0.64 */ | ||
699 | mixer_vth_l = 0x75; /* mixer vth 1.04, vtl 0.84 */ | ||
700 | air_cable1_in = 0x00; | ||
701 | cable2_in = 0x00; | ||
702 | pre_dect = 0x40; | ||
703 | lna_discharge = 14; | ||
704 | cp_cur = 0x38; /* 111, auto */ | ||
705 | div_buf_cur = 0x30; /* 11, 150u */ | ||
706 | filter_cur = 0x40; /* 10, low */ | ||
707 | break; | ||
708 | case SYS_ISDBT: | ||
709 | mixer_top = 0x24; /* mixer top:13 , top-1, low-discharge */ | ||
710 | lna_top = 0xe5; /* detect bw 3, lna top:4, predet top:2 */ | ||
711 | lna_vth_l = 0x75; /* lna vth 1.04 , vtl 0.84 */ | ||
712 | mixer_vth_l = 0x75; /* mixer vth 1.04, vtl 0.84 */ | ||
713 | air_cable1_in = 0x00; | ||
714 | cable2_in = 0x00; | ||
715 | pre_dect = 0x40; | ||
716 | lna_discharge = 14; | ||
717 | cp_cur = 0x38; /* 111, auto */ | ||
718 | div_buf_cur = 0x30; /* 11, 150u */ | ||
719 | filter_cur = 0x40; /* 10, low */ | ||
720 | break; | ||
721 | default: /* DVB-T 8M */ | ||
722 | mixer_top = 0x24; /* mixer top:13 , top-1, low-discharge */ | ||
723 | lna_top = 0xe5; /* detect bw 3, lna top:4, predet top:2 */ | ||
724 | lna_vth_l = 0x53; /* lna vth 0.84 , vtl 0.64 */ | ||
725 | mixer_vth_l = 0x75; /* mixer vth 1.04, vtl 0.84 */ | ||
726 | air_cable1_in = 0x00; | ||
727 | cable2_in = 0x00; | ||
728 | pre_dect = 0x40; | ||
729 | lna_discharge = 14; | ||
730 | cp_cur = 0x38; /* 111, auto */ | ||
731 | div_buf_cur = 0x30; /* 11, 150u */ | ||
732 | filter_cur = 0x40; /* 10, low */ | ||
733 | break; | ||
734 | } | ||
735 | |||
736 | rc = r820t_write_reg_mask(priv, 0x1d, lna_top, 0xc7); | ||
737 | if (rc < 0) | ||
738 | return rc; | ||
739 | rc = r820t_write_reg_mask(priv, 0x1c, mixer_top, 0xf8); | ||
740 | if (rc < 0) | ||
741 | return rc; | ||
742 | rc = r820t_write_reg(priv, 0x0d, lna_vth_l); | ||
743 | if (rc < 0) | ||
744 | return rc; | ||
745 | rc = r820t_write_reg(priv, 0x0e, mixer_vth_l); | ||
746 | if (rc < 0) | ||
747 | return rc; | ||
748 | |||
749 | /* Air-IN only for Astrometa */ | ||
750 | rc = r820t_write_reg_mask(priv, 0x05, air_cable1_in, 0x60); | ||
751 | if (rc < 0) | ||
752 | return rc; | ||
753 | rc = r820t_write_reg_mask(priv, 0x06, cable2_in, 0x08); | ||
754 | if (rc < 0) | ||
755 | return rc; | ||
756 | |||
757 | rc = r820t_write_reg_mask(priv, 0x11, cp_cur, 0x38); | ||
758 | if (rc < 0) | ||
759 | return rc; | ||
760 | rc = r820t_write_reg_mask(priv, 0x17, div_buf_cur, 0x30); | ||
761 | if (rc < 0) | ||
762 | return rc; | ||
763 | rc = r820t_write_reg_mask(priv, 0x0a, filter_cur, 0x60); | ||
764 | if (rc < 0) | ||
765 | return rc; | ||
766 | /* | ||
767 | * Original driver initializes regs 0x05 and 0x06 with the | ||
768 | * same value again on this point. Probably, it is just an | ||
769 | * error there | ||
770 | */ | ||
771 | |||
772 | /* | ||
773 | * Set LNA | ||
774 | */ | ||
775 | |||
776 | tuner_dbg("adjusting LNA parameters\n"); | ||
777 | if (type != V4L2_TUNER_ANALOG_TV) { | ||
778 | /* LNA TOP: lowest */ | ||
779 | rc = r820t_write_reg_mask(priv, 0x1d, 0, 0x38); | ||
780 | if (rc < 0) | ||
781 | return rc; | ||
782 | |||
783 | /* 0: normal mode */ | ||
784 | rc = r820t_write_reg_mask(priv, 0x1c, 0, 0x04); | ||
785 | if (rc < 0) | ||
786 | return rc; | ||
787 | |||
788 | /* 0: PRE_DECT off */ | ||
789 | rc = r820t_write_reg_mask(priv, 0x06, 0, 0x40); | ||
790 | if (rc < 0) | ||
791 | return rc; | ||
792 | |||
793 | /* agc clk 250hz */ | ||
794 | rc = r820t_write_reg_mask(priv, 0x1a, 0x30, 0x30); | ||
795 | if (rc < 0) | ||
796 | return rc; | ||
797 | |||
798 | msleep(250); | ||
799 | |||
800 | /* write LNA TOP = 3 */ | ||
801 | rc = r820t_write_reg_mask(priv, 0x1d, 0x18, 0x38); | ||
802 | if (rc < 0) | ||
803 | return rc; | ||
804 | |||
805 | /* | ||
806 | * write discharge mode | ||
807 | * FIXME: IMHO, the mask here is wrong, but it matches | ||
808 | * what's there at the original driver | ||
809 | */ | ||
810 | rc = r820t_write_reg_mask(priv, 0x1c, mixer_top, 0x04); | ||
811 | if (rc < 0) | ||
812 | return rc; | ||
813 | |||
814 | /* LNA discharge current */ | ||
815 | rc = r820t_write_reg_mask(priv, 0x1e, lna_discharge, 0x1f); | ||
816 | if (rc < 0) | ||
817 | return rc; | ||
818 | |||
819 | /* agc clk 60hz */ | ||
820 | rc = r820t_write_reg_mask(priv, 0x1a, 0x20, 0x30); | ||
821 | if (rc < 0) | ||
822 | return rc; | ||
823 | } else { | ||
824 | /* PRE_DECT off */ | ||
825 | rc = r820t_write_reg_mask(priv, 0x06, 0, 0x40); | ||
826 | if (rc < 0) | ||
827 | return rc; | ||
828 | |||
829 | /* write LNA TOP */ | ||
830 | rc = r820t_write_reg_mask(priv, 0x1d, lna_top, 0x38); | ||
831 | if (rc < 0) | ||
832 | return rc; | ||
833 | |||
834 | /* | ||
835 | * write discharge mode | ||
836 | * FIXME: IMHO, the mask here is wrong, but it matches | ||
837 | * what's there at the original driver | ||
838 | */ | ||
839 | rc = r820t_write_reg_mask(priv, 0x1c, mixer_top, 0x04); | ||
840 | if (rc < 0) | ||
841 | return rc; | ||
842 | |||
843 | /* LNA discharge current */ | ||
844 | rc = r820t_write_reg_mask(priv, 0x1e, lna_discharge, 0x1f); | ||
845 | if (rc < 0) | ||
846 | return rc; | ||
847 | |||
848 | /* agc clk 1Khz, external det1 cap 1u */ | ||
849 | rc = r820t_write_reg_mask(priv, 0x1a, 0x00, 0x30); | ||
850 | if (rc < 0) | ||
851 | return rc; | ||
852 | |||
853 | rc = r820t_write_reg_mask(priv, 0x10, 0x00, 0x04); | ||
854 | if (rc < 0) | ||
855 | return rc; | ||
856 | } | ||
857 | return 0; | ||
858 | } | ||
859 | |||
860 | static int r820t_set_tv_standard(struct r820t_priv *priv, | ||
861 | unsigned bw, | ||
862 | enum v4l2_tuner_type type, | ||
863 | v4l2_std_id std, u32 delsys) | ||
864 | |||
865 | { | ||
866 | int rc, i; | ||
867 | u32 if_khz, filt_cal_lo; | ||
868 | u8 data[5], val; | ||
869 | u8 filt_gain, img_r, filt_q, hp_cor, ext_enable, loop_through; | ||
870 | u8 lt_att, flt_ext_widest, polyfil_cur; | ||
871 | bool need_calibration; | ||
872 | |||
873 | tuner_dbg("selecting the delivery system\n"); | ||
874 | |||
875 | if (delsys == SYS_ISDBT) { | ||
876 | if_khz = 4063; | ||
877 | filt_cal_lo = 59000; | ||
878 | filt_gain = 0x10; /* +3db, 6mhz on */ | ||
879 | img_r = 0x00; /* image negative */ | ||
880 | filt_q = 0x10; /* r10[4]:low q(1'b1) */ | ||
881 | hp_cor = 0x6a; /* 1.7m disable, +2cap, 1.25mhz */ | ||
882 | ext_enable = 0x40; /* r30[6], ext enable; r30[5]:0 ext at lna max */ | ||
883 | loop_through = 0x00; /* r5[7], lt on */ | ||
884 | lt_att = 0x00; /* r31[7], lt att enable */ | ||
885 | flt_ext_widest = 0x00; /* r15[7]: flt_ext_wide off */ | ||
886 | polyfil_cur = 0x60; /* r25[6:5]:min */ | ||
887 | } else { | ||
888 | if (bw <= 6) { | ||
889 | if_khz = 3570; | ||
890 | filt_cal_lo = 56000; /* 52000->56000 */ | ||
891 | filt_gain = 0x10; /* +3db, 6mhz on */ | ||
892 | img_r = 0x00; /* image negative */ | ||
893 | filt_q = 0x10; /* r10[4]:low q(1'b1) */ | ||
894 | hp_cor = 0x6b; /* 1.7m disable, +2cap, 1.0mhz */ | ||
895 | ext_enable = 0x60; /* r30[6]=1 ext enable; r30[5]:1 ext at lna max-1 */ | ||
896 | loop_through = 0x00; /* r5[7], lt on */ | ||
897 | lt_att = 0x00; /* r31[7], lt att enable */ | ||
898 | flt_ext_widest = 0x00; /* r15[7]: flt_ext_wide off */ | ||
899 | polyfil_cur = 0x60; /* r25[6:5]:min */ | ||
900 | } else if (bw == 7) { | ||
901 | if_khz = 4070; | ||
902 | filt_cal_lo = 60000; | ||
903 | filt_gain = 0x10; /* +3db, 6mhz on */ | ||
904 | img_r = 0x00; /* image negative */ | ||
905 | filt_q = 0x10; /* r10[4]:low q(1'b1) */ | ||
906 | hp_cor = 0x2b; /* 1.7m disable, +1cap, 1.0mhz */ | ||
907 | ext_enable = 0x60; /* r30[6]=1 ext enable; r30[5]:1 ext at lna max-1 */ | ||
908 | loop_through = 0x00; /* r5[7], lt on */ | ||
909 | lt_att = 0x00; /* r31[7], lt att enable */ | ||
910 | flt_ext_widest = 0x00; /* r15[7]: flt_ext_wide off */ | ||
911 | polyfil_cur = 0x60; /* r25[6:5]:min */ | ||
912 | #if 0 /* 7 MHz type 2 - nor sure why/where this is used - Perhaps Australia? */ | ||
913 | if_khz = 4570; | ||
914 | filt_cal_lo = 63000; | ||
915 | filt_gain = 0x10; /* +3db, 6mhz on */ | ||
916 | img_r = 0x00; /* image negative */ | ||
917 | filt_q = 0x10; /* r10[4]:low q(1'b1) */ | ||
918 | hp_cor = 0x2a; /* 1.7m disable, +1cap, 1.25mhz */ | ||
919 | ext_enable = 0x60; /* r30[6]=1 ext enable; r30[5]:1 ext at lna max-1 */ | ||
920 | loop_through = 0x00; /* r5[7], lt on */ | ||
921 | lt_att = 0x00; /* r31[7], lt att enable */ | ||
922 | flt_ext_widest = 0x00; /* r15[7]: flt_ext_wide off */ | ||
923 | polyfil_cur = 0x60; /* r25[6:5]:min */ | ||
924 | #endif | ||
925 | } else { | ||
926 | if_khz = 4570; | ||
927 | filt_cal_lo = 68500; | ||
928 | filt_gain = 0x10; /* +3db, 6mhz on */ | ||
929 | img_r = 0x00; /* image negative */ | ||
930 | filt_q = 0x10; /* r10[4]:low q(1'b1) */ | ||
931 | hp_cor = 0x0b; /* 1.7m disable, +0cap, 1.0mhz */ | ||
932 | ext_enable = 0x60; /* r30[6]=1 ext enable; r30[5]:1 ext at lna max-1 */ | ||
933 | loop_through = 0x00; /* r5[7], lt on */ | ||
934 | lt_att = 0x00; /* r31[7], lt att enable */ | ||
935 | flt_ext_widest = 0x00; /* r15[7]: flt_ext_wide off */ | ||
936 | polyfil_cur = 0x60; /* r25[6:5]:min */ | ||
937 | } | ||
938 | } | ||
939 | |||
940 | /* Initialize the shadow registers */ | ||
941 | memcpy(priv->regs, r820t_init_array, sizeof(r820t_init_array)); | ||
942 | |||
943 | /* Init Flag & Xtal_check Result */ | ||
944 | if (priv->imr_done) | ||
945 | val = 1 | priv->xtal_cap_sel << 1; | ||
946 | else | ||
947 | val = 0; | ||
948 | rc = r820t_write_reg_mask(priv, 0x0c, val, 0x0f); | ||
949 | if (rc < 0) | ||
950 | return rc; | ||
951 | |||
952 | /* version */ | ||
953 | rc = r820t_write_reg_mask(priv, 0x13, VER_NUM, 0x3f); | ||
954 | if (rc < 0) | ||
955 | return rc; | ||
956 | |||
957 | /* for LT Gain test */ | ||
958 | if (type != V4L2_TUNER_ANALOG_TV) { | ||
959 | rc = r820t_write_reg_mask(priv, 0x1d, 0x00, 0x38); | ||
960 | if (rc < 0) | ||
961 | return rc; | ||
962 | msleep(1); | ||
963 | } | ||
964 | priv->int_freq = if_khz; | ||
965 | |||
966 | /* Check if standard changed. If so, filter calibration is needed */ | ||
967 | if (type != priv->type) | ||
968 | need_calibration = true; | ||
969 | else if ((type == V4L2_TUNER_ANALOG_TV) && (std != priv->std)) | ||
970 | need_calibration = true; | ||
971 | else if ((type == V4L2_TUNER_DIGITAL_TV) && | ||
972 | ((delsys != priv->delsys) || bw != priv->bw)) | ||
973 | need_calibration = true; | ||
974 | else | ||
975 | need_calibration = false; | ||
976 | |||
977 | if (need_calibration) { | ||
978 | tuner_dbg("calibrating the tuner\n"); | ||
979 | for (i = 0; i < 2; i++) { | ||
980 | /* Set filt_cap */ | ||
981 | rc = r820t_write_reg_mask(priv, 0x0b, hp_cor, 0x60); | ||
982 | if (rc < 0) | ||
983 | return rc; | ||
984 | |||
985 | /* set cali clk =on */ | ||
986 | rc = r820t_write_reg_mask(priv, 0x0f, 0x04, 0x04); | ||
987 | if (rc < 0) | ||
988 | return rc; | ||
989 | |||
990 | /* X'tal cap 0pF for PLL */ | ||
991 | rc = r820t_write_reg_mask(priv, 0x10, 0x00, 0x03); | ||
992 | if (rc < 0) | ||
993 | return rc; | ||
994 | |||
995 | rc = r820t_set_pll(priv, filt_cal_lo); | ||
996 | if (rc < 0 || !priv->has_lock) | ||
997 | return rc; | ||
998 | |||
999 | /* Start Trigger */ | ||
1000 | rc = r820t_write_reg_mask(priv, 0x0b, 0x10, 0x10); | ||
1001 | if (rc < 0) | ||
1002 | return rc; | ||
1003 | |||
1004 | msleep(1); | ||
1005 | |||
1006 | /* Stop Trigger */ | ||
1007 | rc = r820t_write_reg_mask(priv, 0x0b, 0x00, 0x10); | ||
1008 | if (rc < 0) | ||
1009 | return rc; | ||
1010 | |||
1011 | /* set cali clk =off */ | ||
1012 | rc = r820t_write_reg_mask(priv, 0x0f, 0x00, 0x04); | ||
1013 | if (rc < 0) | ||
1014 | return rc; | ||
1015 | |||
1016 | /* Check if calibration worked */ | ||
1017 | rc = r820_read(priv, 0x00, data, sizeof(data)); | ||
1018 | if (rc < 0) | ||
1019 | return rc; | ||
1020 | |||
1021 | priv->fil_cal_code = data[4] & 0x0f; | ||
1022 | if (priv->fil_cal_code && priv->fil_cal_code != 0x0f) | ||
1023 | break; | ||
1024 | } | ||
1025 | /* narrowest */ | ||
1026 | if (priv->fil_cal_code == 0x0f) | ||
1027 | priv->fil_cal_code = 0; | ||
1028 | } | ||
1029 | |||
1030 | rc = r820t_write_reg_mask(priv, 0x0a, | ||
1031 | filt_q | priv->fil_cal_code, 0x1f); | ||
1032 | if (rc < 0) | ||
1033 | return rc; | ||
1034 | |||
1035 | /* Set BW, Filter_gain, & HP corner */ | ||
1036 | rc = r820t_write_reg_mask(priv, 0x0b, hp_cor, 0x10); | ||
1037 | if (rc < 0) | ||
1038 | return rc; | ||
1039 | |||
1040 | |||
1041 | /* Set Img_R */ | ||
1042 | rc = r820t_write_reg_mask(priv, 0x07, img_r, 0x80); | ||
1043 | if (rc < 0) | ||
1044 | return rc; | ||
1045 | |||
1046 | /* Set filt_3dB, V6MHz */ | ||
1047 | rc = r820t_write_reg_mask(priv, 0x06, filt_gain, 0x30); | ||
1048 | if (rc < 0) | ||
1049 | return rc; | ||
1050 | |||
1051 | /* channel filter extension */ | ||
1052 | rc = r820t_write_reg_mask(priv, 0x1e, ext_enable, 0x60); | ||
1053 | if (rc < 0) | ||
1054 | return rc; | ||
1055 | |||
1056 | /* Loop through */ | ||
1057 | rc = r820t_write_reg_mask(priv, 0x05, loop_through, 0x80); | ||
1058 | if (rc < 0) | ||
1059 | return rc; | ||
1060 | |||
1061 | /* Loop through attenuation */ | ||
1062 | rc = r820t_write_reg_mask(priv, 0x1f, lt_att, 0x80); | ||
1063 | if (rc < 0) | ||
1064 | return rc; | ||
1065 | |||
1066 | /* filter extension widest */ | ||
1067 | rc = r820t_write_reg_mask(priv, 0x0f, flt_ext_widest, 0x80); | ||
1068 | if (rc < 0) | ||
1069 | return rc; | ||
1070 | |||
1071 | /* RF poly filter current */ | ||
1072 | rc = r820t_write_reg_mask(priv, 0x19, polyfil_cur, 0x60); | ||
1073 | if (rc < 0) | ||
1074 | return rc; | ||
1075 | |||
1076 | /* Store current standard. If it changes, re-calibrate the tuner */ | ||
1077 | priv->delsys = delsys; | ||
1078 | priv->type = type; | ||
1079 | priv->std = std; | ||
1080 | priv->bw = bw; | ||
1081 | |||
1082 | return 0; | ||
1083 | } | ||
1084 | |||
1085 | static int generic_set_freq(struct dvb_frontend *fe, | ||
1086 | u32 freq /* in HZ */, | ||
1087 | unsigned bw, | ||
1088 | enum v4l2_tuner_type type, | ||
1089 | v4l2_std_id std, u32 delsys) | ||
1090 | { | ||
1091 | struct r820t_priv *priv = fe->tuner_priv; | ||
1092 | int rc = -EINVAL; | ||
1093 | u32 lo_freq; | ||
1094 | |||
1095 | tuner_dbg("should set frequency to %d kHz, bw %d MHz\n", | ||
1096 | freq / 1000, bw); | ||
1097 | |||
1098 | mutex_lock(&priv->lock); | ||
1099 | |||
1100 | if ((type == V4L2_TUNER_ANALOG_TV) && (std == V4L2_STD_SECAM_LC)) | ||
1101 | lo_freq = freq - priv->int_freq; | ||
1102 | else | ||
1103 | lo_freq = freq + priv->int_freq; | ||
1104 | |||
1105 | rc = r820t_set_tv_standard(priv, bw, type, std, delsys); | ||
1106 | if (rc < 0) | ||
1107 | goto err; | ||
1108 | |||
1109 | rc = r820t_set_mux(priv, lo_freq); | ||
1110 | if (rc < 0) | ||
1111 | goto err; | ||
1112 | rc = r820t_set_pll(priv, lo_freq); | ||
1113 | if (rc < 0 || !priv->has_lock) | ||
1114 | goto err; | ||
1115 | |||
1116 | rc = r820t_sysfreq_sel(priv, freq, type, std, delsys); | ||
1117 | err: | ||
1118 | mutex_unlock(&priv->lock); | ||
1119 | |||
1120 | if (rc < 0) | ||
1121 | tuner_dbg("%s: failed=%d\n", __func__, rc); | ||
1122 | return rc; | ||
1123 | } | ||
1124 | |||
1125 | /* | ||
1126 | * r820t standby logic | ||
1127 | */ | ||
1128 | |||
1129 | static int r820t_standby(struct r820t_priv *priv) | ||
1130 | { | ||
1131 | int rc; | ||
1132 | |||
1133 | rc = r820t_write_reg(priv, 0x06, 0xb1); | ||
1134 | if (rc < 0) | ||
1135 | return rc; | ||
1136 | rc = r820t_write_reg(priv, 0x05, 0x03); | ||
1137 | if (rc < 0) | ||
1138 | return rc; | ||
1139 | rc = r820t_write_reg(priv, 0x07, 0x3a); | ||
1140 | if (rc < 0) | ||
1141 | return rc; | ||
1142 | rc = r820t_write_reg(priv, 0x08, 0x40); | ||
1143 | if (rc < 0) | ||
1144 | return rc; | ||
1145 | rc = r820t_write_reg(priv, 0x09, 0xc0); | ||
1146 | if (rc < 0) | ||
1147 | return rc; | ||
1148 | rc = r820t_write_reg(priv, 0x0a, 0x36); | ||
1149 | if (rc < 0) | ||
1150 | return rc; | ||
1151 | rc = r820t_write_reg(priv, 0x0c, 0x35); | ||
1152 | if (rc < 0) | ||
1153 | return rc; | ||
1154 | rc = r820t_write_reg(priv, 0x0f, 0x68); | ||
1155 | if (rc < 0) | ||
1156 | return rc; | ||
1157 | rc = r820t_write_reg(priv, 0x11, 0x03); | ||
1158 | if (rc < 0) | ||
1159 | return rc; | ||
1160 | rc = r820t_write_reg(priv, 0x17, 0xf4); | ||
1161 | if (rc < 0) | ||
1162 | return rc; | ||
1163 | rc = r820t_write_reg(priv, 0x19, 0x0c); | ||
1164 | |||
1165 | /* Force initial calibration */ | ||
1166 | priv->type = -1; | ||
1167 | |||
1168 | return rc; | ||
1169 | } | ||
1170 | |||
1171 | /* | ||
1172 | * r820t device init logic | ||
1173 | */ | ||
1174 | |||
1175 | static int r820t_xtal_check(struct r820t_priv *priv) | ||
1176 | { | ||
1177 | int rc, i; | ||
1178 | u8 data[3], val; | ||
1179 | |||
1180 | /* Initialize the shadow registers */ | ||
1181 | memcpy(priv->regs, r820t_init_array, sizeof(r820t_init_array)); | ||
1182 | |||
1183 | /* cap 30pF & Drive Low */ | ||
1184 | rc = r820t_write_reg_mask(priv, 0x10, 0x0b, 0x0b); | ||
1185 | if (rc < 0) | ||
1186 | return rc; | ||
1187 | |||
1188 | /* set pll autotune = 128kHz */ | ||
1189 | rc = r820t_write_reg_mask(priv, 0x1a, 0x00, 0x0c); | ||
1190 | if (rc < 0) | ||
1191 | return rc; | ||
1192 | |||
1193 | /* set manual initial reg = 111111; */ | ||
1194 | rc = r820t_write_reg_mask(priv, 0x13, 0x7f, 0x7f); | ||
1195 | if (rc < 0) | ||
1196 | return rc; | ||
1197 | |||
1198 | /* set auto */ | ||
1199 | rc = r820t_write_reg_mask(priv, 0x13, 0x00, 0x40); | ||
1200 | if (rc < 0) | ||
1201 | return rc; | ||
1202 | |||
1203 | /* Try several xtal capacitor alternatives */ | ||
1204 | for (i = 0; i < ARRAY_SIZE(r820t_xtal_capacitor); i++) { | ||
1205 | rc = r820t_write_reg_mask(priv, 0x10, | ||
1206 | r820t_xtal_capacitor[i][0], 0x1b); | ||
1207 | if (rc < 0) | ||
1208 | return rc; | ||
1209 | |||
1210 | msleep(5); | ||
1211 | |||
1212 | rc = r820_read(priv, 0x00, data, sizeof(data)); | ||
1213 | if (rc < 0) | ||
1214 | return rc; | ||
1215 | if ((!data[2]) & 0x40) | ||
1216 | continue; | ||
1217 | |||
1218 | val = data[2] & 0x3f; | ||
1219 | |||
1220 | if (priv->cfg->xtal == 16000000 && (val > 29 || val < 23)) | ||
1221 | break; | ||
1222 | |||
1223 | if (val != 0x3f) | ||
1224 | break; | ||
1225 | } | ||
1226 | |||
1227 | if (i == ARRAY_SIZE(r820t_xtal_capacitor)) | ||
1228 | return -EINVAL; | ||
1229 | |||
1230 | return r820t_xtal_capacitor[i][1]; | ||
1231 | } | ||
1232 | |||
1233 | /* | ||
1234 | * r820t frontend operations and tuner attach code | ||
1235 | */ | ||
1236 | |||
1237 | static int r820t_init(struct dvb_frontend *fe) | ||
1238 | { | ||
1239 | struct r820t_priv *priv = fe->tuner_priv; | ||
1240 | int rc, i; | ||
1241 | int xtal_cap = 0; | ||
1242 | |||
1243 | tuner_dbg("%s:\n", __func__); | ||
1244 | |||
1245 | if (fe->ops.i2c_gate_ctrl) | ||
1246 | fe->ops.i2c_gate_ctrl(fe, 1); | ||
1247 | |||
1248 | mutex_lock(&priv->lock); | ||
1249 | |||
1250 | if ((priv->cfg->rafael_chip == CHIP_R820T) || | ||
1251 | (priv->cfg->rafael_chip == CHIP_R828S) || | ||
1252 | (priv->cfg->rafael_chip == CHIP_R820C)) { | ||
1253 | priv->xtal_cap_sel = XTAL_HIGH_CAP_0P; | ||
1254 | } else { | ||
1255 | for (i = 0; i < 3; i++) { | ||
1256 | rc = r820t_xtal_check(priv); | ||
1257 | if (rc < 0) | ||
1258 | goto err; | ||
1259 | if (!i || rc > xtal_cap) | ||
1260 | xtal_cap = rc; | ||
1261 | } | ||
1262 | priv->xtal_cap_sel = xtal_cap; | ||
1263 | } | ||
1264 | |||
1265 | /* Initialize registers */ | ||
1266 | rc = r820t_write(priv, 0x05, | ||
1267 | r820t_init_array, sizeof(r820t_init_array)); | ||
1268 | |||
1269 | mutex_unlock(&priv->lock); | ||
1270 | |||
1271 | if (fe->ops.i2c_gate_ctrl) | ||
1272 | fe->ops.i2c_gate_ctrl(fe, 0); | ||
1273 | |||
1274 | return rc; | ||
1275 | err: | ||
1276 | if (fe->ops.i2c_gate_ctrl) | ||
1277 | fe->ops.i2c_gate_ctrl(fe, 0); | ||
1278 | |||
1279 | tuner_dbg("%s: failed=%d\n", __func__, rc); | ||
1280 | return rc; | ||
1281 | } | ||
1282 | |||
1283 | static int r820t_sleep(struct dvb_frontend *fe) | ||
1284 | { | ||
1285 | struct r820t_priv *priv = fe->tuner_priv; | ||
1286 | int rc; | ||
1287 | |||
1288 | tuner_dbg("%s:\n", __func__); | ||
1289 | |||
1290 | if (fe->ops.i2c_gate_ctrl) | ||
1291 | fe->ops.i2c_gate_ctrl(fe, 1); | ||
1292 | |||
1293 | mutex_lock(&priv->lock); | ||
1294 | rc = r820t_standby(priv); | ||
1295 | mutex_unlock(&priv->lock); | ||
1296 | |||
1297 | if (fe->ops.i2c_gate_ctrl) | ||
1298 | fe->ops.i2c_gate_ctrl(fe, 0); | ||
1299 | |||
1300 | tuner_dbg("%s: failed=%d\n", __func__, rc); | ||
1301 | return rc; | ||
1302 | } | ||
1303 | |||
1304 | static int r820t_set_analog_freq(struct dvb_frontend *fe, | ||
1305 | struct analog_parameters *p) | ||
1306 | { | ||
1307 | struct r820t_priv *priv = fe->tuner_priv; | ||
1308 | unsigned bw; | ||
1309 | |||
1310 | tuner_dbg("%s called\n", __func__); | ||
1311 | |||
1312 | /* if std is not defined, choose one */ | ||
1313 | if (!p->std) | ||
1314 | p->std = V4L2_STD_MN; | ||
1315 | |||
1316 | if ((p->std == V4L2_STD_PAL_M) || (p->std == V4L2_STD_NTSC)) | ||
1317 | bw = 6; | ||
1318 | else | ||
1319 | bw = 8; | ||
1320 | |||
1321 | return generic_set_freq(fe, 62500l * p->frequency, bw, | ||
1322 | V4L2_TUNER_ANALOG_TV, p->std, SYS_UNDEFINED); | ||
1323 | } | ||
1324 | |||
1325 | static int r820t_set_params(struct dvb_frontend *fe) | ||
1326 | { | ||
1327 | struct r820t_priv *priv = fe->tuner_priv; | ||
1328 | struct dtv_frontend_properties *c = &fe->dtv_property_cache; | ||
1329 | int rc; | ||
1330 | unsigned bw; | ||
1331 | |||
1332 | tuner_dbg("%s: delivery_system=%d frequency=%d bandwidth_hz=%d\n", | ||
1333 | __func__, c->delivery_system, c->frequency, c->bandwidth_hz); | ||
1334 | |||
1335 | if (fe->ops.i2c_gate_ctrl) | ||
1336 | fe->ops.i2c_gate_ctrl(fe, 1); | ||
1337 | |||
1338 | bw = (c->bandwidth_hz + 500000) / 1000000; | ||
1339 | if (!bw) | ||
1340 | bw = 8; | ||
1341 | |||
1342 | rc = generic_set_freq(fe, c->frequency, bw, | ||
1343 | V4L2_TUNER_DIGITAL_TV, 0, c->delivery_system); | ||
1344 | |||
1345 | if (fe->ops.i2c_gate_ctrl) | ||
1346 | fe->ops.i2c_gate_ctrl(fe, 0); | ||
1347 | |||
1348 | if (rc) | ||
1349 | tuner_dbg("%s: failed=%d\n", __func__, rc); | ||
1350 | return rc; | ||
1351 | } | ||
1352 | |||
1353 | static int r820t_signal(struct dvb_frontend *fe, u16 *strength) | ||
1354 | { | ||
1355 | struct r820t_priv *priv = fe->tuner_priv; | ||
1356 | |||
1357 | if (priv->has_lock) | ||
1358 | *strength = 0xffff; | ||
1359 | else | ||
1360 | *strength = 0; | ||
1361 | |||
1362 | return 0; | ||
1363 | } | ||
1364 | |||
1365 | static int r820t_get_if_frequency(struct dvb_frontend *fe, u32 *frequency) | ||
1366 | { | ||
1367 | struct r820t_priv *priv = fe->tuner_priv; | ||
1368 | |||
1369 | tuner_dbg("%s:\n", __func__); | ||
1370 | |||
1371 | *frequency = priv->int_freq; | ||
1372 | |||
1373 | return 0; | ||
1374 | } | ||
1375 | |||
1376 | static int r820t_release(struct dvb_frontend *fe) | ||
1377 | { | ||
1378 | struct r820t_priv *priv = fe->tuner_priv; | ||
1379 | |||
1380 | tuner_dbg("%s:\n", __func__); | ||
1381 | |||
1382 | mutex_lock(&r820t_list_mutex); | ||
1383 | |||
1384 | if (priv) | ||
1385 | hybrid_tuner_release_state(priv); | ||
1386 | |||
1387 | mutex_unlock(&r820t_list_mutex); | ||
1388 | |||
1389 | fe->tuner_priv = NULL; | ||
1390 | |||
1391 | kfree(fe->tuner_priv); | ||
1392 | |||
1393 | return 0; | ||
1394 | } | ||
1395 | |||
1396 | static const struct dvb_tuner_ops r820t_tuner_ops = { | ||
1397 | .info = { | ||
1398 | .name = "Rafael Micro R820T", | ||
1399 | .frequency_min = 42000000, | ||
1400 | .frequency_max = 1002000000, | ||
1401 | }, | ||
1402 | .init = r820t_init, | ||
1403 | .release = r820t_release, | ||
1404 | .sleep = r820t_sleep, | ||
1405 | .set_params = r820t_set_params, | ||
1406 | .set_analog_params = r820t_set_analog_freq, | ||
1407 | .get_if_frequency = r820t_get_if_frequency, | ||
1408 | .get_rf_strength = r820t_signal, | ||
1409 | }; | ||
1410 | |||
1411 | struct dvb_frontend *r820t_attach(struct dvb_frontend *fe, | ||
1412 | struct i2c_adapter *i2c, | ||
1413 | const struct r820t_config *cfg) | ||
1414 | { | ||
1415 | struct r820t_priv *priv; | ||
1416 | int rc = -ENODEV; | ||
1417 | u8 data[5]; | ||
1418 | int instance; | ||
1419 | |||
1420 | mutex_lock(&r820t_list_mutex); | ||
1421 | |||
1422 | instance = hybrid_tuner_request_state(struct r820t_priv, priv, | ||
1423 | hybrid_tuner_instance_list, | ||
1424 | i2c, cfg->i2c_addr, | ||
1425 | "r820t"); | ||
1426 | switch (instance) { | ||
1427 | case 0: | ||
1428 | /* memory allocation failure */ | ||
1429 | goto err_no_gate; | ||
1430 | break; | ||
1431 | case 1: | ||
1432 | /* new tuner instance */ | ||
1433 | priv->cfg = cfg; | ||
1434 | |||
1435 | mutex_init(&priv->lock); | ||
1436 | |||
1437 | fe->tuner_priv = priv; | ||
1438 | break; | ||
1439 | case 2: | ||
1440 | /* existing tuner instance */ | ||
1441 | fe->tuner_priv = priv; | ||
1442 | break; | ||
1443 | } | ||
1444 | |||
1445 | memcpy(&fe->ops.tuner_ops, &r820t_tuner_ops, sizeof(r820t_tuner_ops)); | ||
1446 | |||
1447 | if (fe->ops.i2c_gate_ctrl) | ||
1448 | fe->ops.i2c_gate_ctrl(fe, 1); | ||
1449 | |||
1450 | /* check if the tuner is there */ | ||
1451 | rc = r820_read(priv, 0x00, data, sizeof(data)); | ||
1452 | if (rc < 0) | ||
1453 | goto err; | ||
1454 | |||
1455 | rc = r820t_sleep(fe); | ||
1456 | if (rc < 0) | ||
1457 | goto err; | ||
1458 | |||
1459 | tuner_info("Rafael Micro r820t successfully identified\n"); | ||
1460 | |||
1461 | fe->tuner_priv = priv; | ||
1462 | memcpy(&fe->ops.tuner_ops, &r820t_tuner_ops, | ||
1463 | sizeof(struct dvb_tuner_ops)); | ||
1464 | |||
1465 | if (fe->ops.i2c_gate_ctrl) | ||
1466 | fe->ops.i2c_gate_ctrl(fe, 0); | ||
1467 | |||
1468 | mutex_unlock(&r820t_list_mutex); | ||
1469 | |||
1470 | return fe; | ||
1471 | err: | ||
1472 | if (fe->ops.i2c_gate_ctrl) | ||
1473 | fe->ops.i2c_gate_ctrl(fe, 0); | ||
1474 | |||
1475 | err_no_gate: | ||
1476 | mutex_unlock(&r820t_list_mutex); | ||
1477 | |||
1478 | tuner_info("%s: failed=%d\n", __func__, rc); | ||
1479 | r820t_release(fe); | ||
1480 | return NULL; | ||
1481 | } | ||
1482 | EXPORT_SYMBOL_GPL(r820t_attach); | ||
1483 | |||
1484 | MODULE_DESCRIPTION("Rafael Micro r820t silicon tuner driver"); | ||
1485 | MODULE_AUTHOR("Mauro Carvalho Chehab <mchehab@redhat.com>"); | ||
1486 | MODULE_LICENSE("GPL"); | ||
diff --git a/drivers/media/tuners/r820t.h b/drivers/media/tuners/r820t.h new file mode 100644 index 000000000000..a64a7b630729 --- /dev/null +++ b/drivers/media/tuners/r820t.h | |||
@@ -0,0 +1,55 @@ | |||
1 | /* | ||
2 | * Elonics R820T silicon tuner driver | ||
3 | * | ||
4 | * Copyright (C) 2012 Antti Palosaari <crope@iki.fi> | ||
5 | * | ||
6 | * This program is free software; you can redistribute it and/or modify | ||
7 | * it under the terms of the GNU General Public License as published by | ||
8 | * the Free Software Foundation; either version 2 of the License, or | ||
9 | * (at your option) any later version. | ||
10 | * | ||
11 | * This program is distributed in the hope that it will be useful, | ||
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
14 | * GNU General Public License for more details. | ||
15 | * | ||
16 | * You should have received a copy of the GNU General Public License along | ||
17 | * with this program; if not, write to the Free Software Foundation, Inc., | ||
18 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. | ||
19 | */ | ||
20 | |||
21 | #ifndef R820T_H | ||
22 | #define R820T_H | ||
23 | |||
24 | #include <linux/kconfig.h> | ||
25 | #include "dvb_frontend.h" | ||
26 | |||
27 | enum r820t_chip { | ||
28 | CHIP_R820T, | ||
29 | CHIP_R828S, | ||
30 | CHIP_R820C, | ||
31 | }; | ||
32 | |||
33 | struct r820t_config { | ||
34 | u8 i2c_addr; /* 0x34 */ | ||
35 | |||
36 | u32 xtal; | ||
37 | enum r820t_chip rafael_chip; | ||
38 | unsigned max_i2c_msg_len; | ||
39 | }; | ||
40 | |||
41 | #if IS_ENABLED(CONFIG_MEDIA_TUNER_R820T) | ||
42 | struct dvb_frontend *r820t_attach(struct dvb_frontend *fe, | ||
43 | struct i2c_adapter *i2c, | ||
44 | const struct r820t_config *cfg); | ||
45 | #else | ||
46 | static inline struct dvb_frontend *r820t_attach(struct dvb_frontend *fe, | ||
47 | struct i2c_adapter *i2c, | ||
48 | const struct r820t_config *cfg) | ||
49 | { | ||
50 | pr_warn("%s: driver disabled by Kconfig\n", __func__); | ||
51 | return NULL; | ||
52 | } | ||
53 | #endif | ||
54 | |||
55 | #endif | ||