Commit | Line | Data |
---|---|---|
6cb45879 MCC |
1 | /* tuner-xc2028 |
2 | * | |
3 | * Copyright (c) 2007 Mauro Carvalho Chehab (mchehab@infradead.org) | |
4 | * This code is placed under the terms of the GNU General Public License v2 | |
5 | */ | |
6 | ||
7 | #include <linux/i2c.h> | |
8 | #include <asm/div64.h> | |
9 | #include <linux/firmware.h> | |
10 | #include <linux/videodev.h> | |
11 | #include <linux/delay.h> | |
12 | #include "tuner-driver.h" | |
13 | #include "tuner-xc2028.h" | |
14 | ||
15 | /* Firmwares used on tm5600/tm6000 + xc2028/xc3028 */ | |
16 | static const char *firmware_6M = "tm6000_xc3028_DTV_6M.fw"; | |
17 | static const char *firmware_8M = "tm6000_xc3028_78M.fw"; | |
18 | static const char *firmware_DK = "tm6000_xc3028_DK_PAL_MTS.fw"; | |
19 | static const char *firmware_MN = "tm6000_xc3028_MN_BTSC.fw"; | |
20 | ||
21 | struct xc2028_data { | |
22 | v4l2_std_id firm_type; /* video stds supported by current firmware */ | |
23 | int bandwidth; /* Firmware bandwidth: 6M, 7M or 8M */ | |
24 | int need_load_generic; /* The generic firmware were loaded? */ | |
25 | }; | |
26 | ||
27 | #define i2c_send(rc,c,buf,size) \ | |
28 | if (size != (rc = i2c_master_send(c, buf, size))) \ | |
29 | tuner_warn("i2c output error: rc = %d (should be %d)\n", \ | |
30 | rc, (int)size); | |
31 | ||
32 | #define i2c_rcv(rc,c,buf,size) \ | |
33 | if (size != (rc = i2c_master_recv(c, buf, size))) \ | |
34 | tuner_warn("i2c input error: rc = %d (should be %d)\n", \ | |
35 | rc, (int)size); | |
36 | ||
37 | #define send_seq(c, data...) \ | |
38 | { int rc; \ | |
39 | const static u8 _val[] = data; \ | |
40 | if (sizeof(_val) != \ | |
41 | (rc = i2c_master_send \ | |
42 | (c, _val, sizeof(_val)))) { \ | |
43 | printk(KERN_ERR "Error on line %d: %d\n",__LINE__,rc); \ | |
44 | return; \ | |
45 | } \ | |
46 | msleep (10); \ | |
47 | } | |
48 | ||
49 | static int xc2028_get_reg(struct i2c_client *c, u16 reg) | |
50 | { | |
51 | int rc; | |
52 | unsigned char buf[1]; | |
53 | struct tuner *t = i2c_get_clientdata(c); | |
54 | ||
55 | buf[0]= reg; | |
56 | ||
57 | i2c_send(rc, c, buf, sizeof(buf)); | |
58 | if (rc<0) | |
59 | return rc; | |
60 | ||
61 | if (t->tuner_callback) { | |
62 | rc = t->tuner_callback( c->adapter->algo_data, | |
63 | XC2028_RESET_CLK, 0); | |
64 | if (rc<0) | |
65 | return rc; | |
66 | } | |
67 | ||
68 | i2c_rcv(rc, c, buf, 2); | |
69 | if (rc<0) | |
70 | return rc; | |
71 | ||
72 | return (buf[1])|(buf[0]<<8); | |
73 | } | |
74 | ||
75 | static int load_firmware (struct i2c_client *c, const char *name) | |
76 | { | |
77 | const struct firmware *fw=NULL; | |
78 | struct tuner *t = i2c_get_clientdata(c); | |
79 | unsigned char *p, *endp; | |
80 | int len=0, rc=0; | |
81 | static const char firmware_ver[] = "tm6000/xcv v1"; | |
82 | ||
83 | tuner_info("Loading firmware %s\n", name); | |
84 | rc = request_firmware(&fw, name, &c->dev); | |
85 | if (rc < 0) { | |
86 | tuner_info("Error %d while requesting firmware\n", rc); | |
87 | return rc; | |
88 | } | |
89 | p=fw->data; | |
90 | endp=p+fw->size; | |
91 | ||
92 | if(fw->size==0) { | |
93 | tuner_info("Error: firmware size is zero!\n"); | |
94 | rc=-EINVAL; | |
95 | goto err; | |
96 | } | |
97 | if (fw->size<sizeof(firmware_ver)-1) { | |
98 | /* Firmware is incorrect */ | |
99 | tuner_info("Error: firmware size is less than header (%d<%d)!\n", | |
100 | (int)fw->size,(int)sizeof(firmware_ver)-1); | |
101 | rc=-EINVAL; | |
102 | goto err; | |
103 | } | |
104 | ||
105 | if (memcmp(p,firmware_ver,sizeof(firmware_ver)-1)) { | |
106 | /* Firmware is incorrect */ | |
107 | tuner_info("Error: firmware is not for tm5600/6000 + Xcv2028/3028!\n"); | |
108 | rc=-EINVAL; | |
109 | goto err; | |
110 | } | |
111 | p+=sizeof(firmware_ver)-1; | |
112 | ||
113 | while(p<endp) { | |
114 | if ((*p) & 0x80) { | |
115 | /* Special callback command received */ | |
116 | rc = t->tuner_callback(c->adapter->algo_data, | |
117 | XC2028_TUNER_RESET, (*p)&0x7f); | |
118 | if (rc<0) { | |
119 | tuner_info("Error at RESET code %d\n", | |
120 | (*p)&0x7f); | |
121 | goto err; | |
122 | } | |
123 | p++; | |
124 | continue; | |
125 | } | |
126 | len=*p; | |
127 | p++; | |
128 | if (p+len+1>endp) { | |
129 | /* Firmware is incorrect */ | |
130 | tuner_info("Error: firmware is truncated!\n"); | |
131 | rc=-EINVAL; | |
132 | goto err; | |
133 | } | |
134 | if (len<=0) { | |
135 | tuner_info("Error: firmware file is corrupted!\n"); | |
136 | rc=-EINVAL; | |
137 | goto err; | |
138 | } | |
139 | ||
140 | i2c_send(rc, c, p, len); | |
141 | if (rc<0) | |
142 | goto err; | |
143 | p+=len; | |
144 | ||
145 | if (*p) | |
146 | msleep(*p); | |
147 | p++; | |
148 | } | |
149 | ||
150 | ||
151 | err: | |
152 | release_firmware(fw); | |
153 | ||
154 | return rc; | |
155 | } | |
156 | ||
157 | static int check_firmware(struct i2c_client *c) | |
158 | { | |
159 | int rc, version; | |
160 | struct tuner *t = i2c_get_clientdata(c); | |
161 | struct xc2028_data *xc2028 = t->priv; | |
162 | const char *name; | |
163 | ||
164 | if (!t->tuner_callback) { | |
165 | printk(KERN_ERR "xc2028: need tuner_callback to load firmware\n"); | |
166 | return -EINVAL; | |
167 | } | |
168 | ||
169 | if (xc2028->need_load_generic) { | |
170 | if (xc2028->bandwidth==6) | |
171 | name = firmware_6M; | |
172 | else | |
173 | name = firmware_8M; | |
174 | ||
175 | /* Reset is needed before loading firmware */ | |
176 | rc = t->tuner_callback(c->adapter->algo_data, | |
177 | XC2028_TUNER_RESET, 0); | |
178 | if (rc<0) | |
179 | return rc; | |
180 | ||
181 | rc = load_firmware(c,name); | |
182 | if (rc<0) | |
183 | return rc; | |
184 | ||
185 | xc2028->need_load_generic=0; | |
186 | xc2028->firm_type=0; | |
187 | } | |
188 | ||
189 | if (xc2028->firm_type & t->std) | |
190 | return 0; | |
191 | ||
192 | if (t->std & V4L2_STD_MN) | |
193 | name=firmware_MN; | |
194 | else | |
195 | name=firmware_DK; | |
196 | ||
197 | rc = load_firmware(c,name); | |
198 | if (rc<0) | |
199 | return rc; | |
200 | ||
201 | version = xc2028_get_reg(c, 0x4); | |
202 | tuner_info("Firmware version is %d.%d\n", | |
203 | (version>>4)&0x0f,(version)&0x0f); | |
204 | ||
205 | xc2028->firm_type=t->std; | |
206 | ||
207 | return 0; | |
208 | } | |
209 | ||
210 | static int xc2028_signal(struct i2c_client *c) | |
211 | { | |
212 | int lock, signal; | |
213 | ||
214 | if (check_firmware(c)<0) | |
215 | return 0; | |
216 | ||
217 | lock = xc2028_get_reg(c, 0x2); | |
218 | if (lock<=0) | |
219 | return lock; | |
220 | ||
221 | /* Frequency is locked. Return signal quality */ | |
222 | ||
223 | signal = xc2028_get_reg(c, 0x40); | |
224 | ||
225 | if(signal<=0) | |
226 | return lock; | |
227 | ||
228 | return signal; | |
229 | } | |
230 | ||
231 | #define DIV 15625 | |
232 | ||
233 | static void set_tv_freq(struct i2c_client *c, unsigned int freq) | |
234 | { | |
235 | int rc; | |
236 | unsigned char buf[5]; | |
237 | struct tuner *t = i2c_get_clientdata(c); | |
238 | unsigned long div = (freq*62500l+DIV/2)/DIV; | |
239 | ||
240 | if (check_firmware(c)<0) | |
241 | return; | |
242 | ||
243 | /* Reset GPIO 1 */ | |
244 | if (t->tuner_callback) { | |
245 | rc = t->tuner_callback( c->adapter->algo_data, | |
246 | XC2028_TUNER_RESET, 0); | |
247 | if (rc<0) | |
248 | return; | |
249 | } | |
250 | msleep(10); | |
251 | ||
252 | send_seq (c, {0x12, 0x39}); | |
253 | send_seq (c, {0x0c, 0x80, 0xf0, 0xf7, 0x3e, 0x75, 0xc1, 0x8a, 0xe4}); | |
254 | send_seq (c, {0x0c, 0x02, 0x00}); | |
255 | send_seq (c, {0x05, 0x0f, 0xee, 0xaa, 0x5f, 0xea, 0x90}); | |
256 | send_seq (c, {0x06, 0x00, 0x0a, 0x4d, 0x8c, 0xf2, 0xd8, 0xcf, 0x30}); | |
257 | send_seq (c, {0x06, 0x79, 0x9f}); | |
258 | send_seq (c, {0x0b, 0x0d, 0xa4, 0x6c}); | |
259 | send_seq (c, {0x0a, 0x01, 0x67, 0x24, 0x40, 0x08, 0xc3, 0x20, 0x10}); | |
260 | send_seq (c, {0x0a, 0x64, 0x3c, 0xfa, 0xf7, 0xe1, 0x0c, 0x2c}); | |
261 | send_seq (c, {0x09, 0x0b}); | |
262 | send_seq (c, {0x10, 0x13}); | |
263 | send_seq (c, {0x16, 0x12}); | |
264 | send_seq (c, {0x1f, 0x02}); | |
265 | send_seq (c, {0x21, 0x02}); | |
266 | send_seq (c, {0x01, 0x02}); | |
267 | send_seq (c, {0x2b, 0x10}); | |
268 | send_seq (c, {0x02, 0x02}); | |
269 | send_seq (c, {0x02, 0x03}); | |
270 | send_seq (c, {0x00, 0x8c}); | |
271 | ||
272 | send_seq (c, {0x00, 0x01, 0x00, 0x00}); | |
273 | send_seq (c, {0x00, 0xcc, 0x20, 0x06}); | |
274 | send_seq (c, {0x2b, 0x1a}); | |
275 | send_seq (c, {0x2b, 0x1b}); | |
276 | send_seq (c, {0x14, 0x01, 0x1b, 0x19, 0xb5, 0x29, 0xab, 0x09, 0x55}); | |
277 | send_seq (c, {0x14, 0x44, 0x05, 0x65}); | |
278 | send_seq (c, {0x13, 0x18, 0x08, 0x00, 0x00, 0x6c, 0x18, 0x16, 0x8c}); | |
279 | send_seq (c, {0x13, 0x49, 0x2a, 0xab}); | |
280 | send_seq (c, {0x0d, 0x01, 0x4b, 0x03, 0x97, 0x55, 0xc7, 0xd7, 0x00}); | |
281 | send_seq (c, {0x0d, 0xa1, 0xeb, 0x8f, 0x5c}); | |
282 | send_seq (c, {0x1a, 0x00, 0x00, 0x16, 0x8a, 0x40, 0x00, 0x00, 0x00, 0x20}); | |
283 | send_seq (c, {0x2d, 0x01}); | |
284 | send_seq (c, {0x18, 0x00}); | |
285 | send_seq (c, {0x1b, 0x0d, 0x86, 0x51, 0xd2, 0x35, 0xa4, 0x92, 0xa5}); | |
286 | send_seq (c, {0x1b, 0xb5, 0x25, 0x65}); | |
287 | send_seq (c, {0x1d, 0x00}); | |
288 | send_seq (c, {0x0f, 0x00, 0x29, 0x56, 0xb0, 0x00, 0xb6}); | |
289 | send_seq (c, {0x20, 0x00}); | |
290 | send_seq (c, {0x1e, 0x09, 0x02, 0x5b, 0x6c, 0x00, 0x4b, 0x81, 0x56}); | |
291 | send_seq (c, {0x1e, 0x46, 0x69, 0x0b}); | |
292 | send_seq (c, {0x22, 0x32}); | |
293 | send_seq (c, {0x23, 0x0a}); | |
294 | send_seq (c, {0x25, 0x00, 0x09, 0x90, 0x09, 0x06, 0x64, 0x02, 0x41}); | |
295 | send_seq (c, {0x26, 0xcc}); | |
296 | send_seq (c, {0x29, 0x40}); | |
297 | send_seq (c, {0x21, 0x03}); | |
298 | send_seq (c, {0x00, 0x8c}); | |
299 | send_seq (c, {0x00, 0x00, 0x00, 0x00}); | |
300 | ||
301 | /* CMD= Set frequency */ | |
302 | send_seq(c, {0x00, 0x02, 0x00, 0x00}); | |
303 | if (t->tuner_callback) { | |
304 | rc = t->tuner_callback( c->adapter->algo_data, | |
305 | XC2028_RESET_CLK, 1); | |
306 | if (rc<0) | |
307 | return; | |
308 | } | |
309 | ||
310 | msleep(10); | |
311 | // send_seq(c, {0x00, 0x00, 0x10, 0xd0, 0x00}); | |
312 | // msleep(100); | |
313 | buf[0]= 0xff & (div>>24); | |
314 | buf[1]= 0xff & (div>>16); | |
315 | buf[2]= 0xff & (div>>8); | |
316 | buf[3]= 0xff & (div); | |
317 | buf[4]= 0; | |
318 | ||
319 | i2c_send(rc, c, buf, sizeof(buf)); | |
320 | if (rc<0) | |
321 | return; | |
322 | msleep(100); | |
323 | ||
324 | printk("divider= %02x %02x %02x %02x (freq=%d.%02d)\n", | |
325 | buf[1],buf[2],buf[3],buf[4], | |
326 | freq / 16, freq % 16 * 100 / 16); | |
327 | // printk("signal=%d\n",xc2028_signal(c)); | |
328 | } | |
329 | ||
330 | ||
331 | static void xc2028_release(struct i2c_client *c) | |
332 | { | |
333 | struct tuner *t = i2c_get_clientdata(c); | |
334 | ||
335 | kfree(t->priv); | |
336 | t->priv = NULL; | |
337 | } | |
338 | ||
339 | static struct tuner_operations tea5767_tuner_ops = { | |
340 | .set_tv_freq = set_tv_freq, | |
341 | .has_signal = xc2028_signal, | |
342 | .release = xc2028_release, | |
343 | // .is_stereo = xc2028_stereo, | |
344 | }; | |
345 | ||
346 | ||
347 | static int init=0; | |
348 | ||
349 | int xc2028_tuner_init(struct i2c_client *c) | |
350 | { | |
351 | struct tuner *t = i2c_get_clientdata(c); | |
352 | int version = xc2028_get_reg(c, 0x4); | |
353 | int prd_id = xc2028_get_reg(c, 0x8); | |
354 | struct xc2028_data *xc2028; | |
355 | ||
356 | if (init) { | |
357 | printk (KERN_ERR "Module already initialized!\n"); | |
358 | return 0; | |
359 | } | |
360 | init++; | |
361 | ||
362 | xc2028 = kzalloc(sizeof(*xc2028), GFP_KERNEL); | |
363 | if (!xc2028) | |
364 | return -ENOMEM; | |
365 | t->priv = xc2028; | |
366 | ||
367 | #ifdef HACK | |
368 | xc2028->firm_type=1; | |
369 | xc2028->bandwidth=6; | |
370 | #endif | |
371 | xc2028->bandwidth=6; | |
372 | xc2028->need_load_generic=1; | |
373 | ||
374 | /* FIXME: Check where t->priv will be freed */ | |
375 | ||
376 | if (version<0) | |
377 | version=0; | |
378 | ||
379 | if (prd_id<0) | |
380 | prd_id=0; | |
381 | ||
382 | strlcpy(c->name, "xc2028", sizeof(c->name)); | |
383 | tuner_info("type set to %d (%s, hw ver=%d.%d, fw ver=%d.%d, id=0x%04x)\n", | |
384 | t->type, c->name, | |
385 | (version>>12)&0x0f,(version>>8)&0x0f, | |
386 | (version>>4)&0x0f,(version)&0x0f, prd_id); | |
387 | ||
388 | memcpy(&t->ops, &tea5767_tuner_ops, sizeof(struct tuner_operations)); | |
389 | ||
390 | return 0; | |
391 | } |