1 /*
2 * Copyright (c) 2003, 2004, 2005
3 * John Wehle <john@feith.com>. All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
13 * 3. All advertising materials mentioning features or use of this software
14 * must display the following acknowledgement:
15 * This product includes software developed by John Wehle.
16 * 4. The name of the author may not be used to endorse or promote products
17 * derived from this software without specific prior written permission.
18 *
19 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
20 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
21 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
22 * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
23 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
24 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
25 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
27 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
28 * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29 * POSSIBILITY OF SUCH DAMAGE.
30 */
31
32 /*
33 * Tuner routines for the Conexant MPEG-2 Codec driver.
34 *
35 * Ideally these routines should be implemented as a separate
36 * driver which has a generic tuner interface so that it's
37 * not necessary for each multimedia driver to re-invent the
38 * wheel.
39 */
40
41 #include <sys/param.h>
42 #include <sys/systm.h>
43 #include <sys/conf.h>
44 #include <sys/uio.h>
45 #include <sys/kernel.h>
46 #include <sys/poll.h>
47 #include <sys/select.h>
48 #include <sys/resource.h>
49 #include <sys/bus.h>
50 #include <sys/rman.h>
51
52 #include <machine/clock.h>
53
54 #include <dev/video/meteor/ioctl_meteor.h>
55 #include <dev/video/bktr/ioctl_bt848.h>
56
57 #include <dev/video/cxm/cxm.h>
58
59 #include <bus/iicbus/iiconf.h>
60 #include <bus/iicbus/iicbus.h>
61
62 #include "iicbb_if.h"
63
64
65 /*
66 * Channel mappings derived from
67 * http://developer.apple.com/technotes/tn/tn1012.html
68 *
69 * B/G Cable mapping based the bktr Western European mapping
70 */
71
72 static const struct cxm_tuner_channels
73 us_air_channels = {
74 "US Broadcast",
75 CHNLSET_NABCST,
76 CXM_TUNER_TV_SYSTEM_MN,
77 2,
78 69,
79 45750,
80 { { 14, 471250, 6000 },
81 { 7, 175250, 6000 },
82 { 5, 77250, 6000 },
83 { 2, 55250, 6000 } }
84 };
85
86 static const struct cxm_tuner_channels
87 us_cable_channels = {
88 "US Cable",
89 CHNLSET_CABLEIRC,
90 CXM_TUNER_TV_SYSTEM_MN,
91 1,
92 125,
93 45750,
94 { { 100, 649250, 6000 },
95 { 95, 91250, 6000 },
96 { 23, 217250, 6000 },
97 { 14, 121250, 6000 },
98 { 7, 175250, 6000 },
99 { 5, 77250, 6000 },
100 { 2, 55250, 6000 },
101 { 1, 73250, 6000 } }
102 };
103
104 static const struct cxm_tuner_channels
105 bg_air_channels = {
106 "B/G Broadcast",
107 0,
108 CXM_TUNER_TV_SYSTEM_BG,
109 2,
110 89,
111 38900,
112 { { 82, 175250, 7000 },
113 { 80, 55250, 7000 },
114 { 79, 217250, 7000 },
115 { 77, 209250, 7000 },
116 { 76, 138250, 9000 },
117 { 75, 102250, 9000 },
118 { 73, 86250, 9000 },
119 { 72, 64250, 7500 },
120 { 70, 49750, 7500 },
121 { 21, 471250, 8000 },
122 { 20, 210250, 8500 },
123 { 18, 192750, 8500 },
124 { 16, 175250, 8000 },
125 { 15, 82250, 8500 },
126 { 13, 53750, 8500 },
127 { 5, 175250, 7000 },
128 { 2, 48250, 7000 } }
129 };
130
131 static const struct cxm_tuner_channels
132 bg_cable_channels = {
133 "B/G Cable",
134 CHNLSET_WEUROPE,
135 CXM_TUNER_TV_SYSTEM_BG,
136 2,
137 120,
138 38900,
139 { { 100, 303250, 8000 },
140 { 90, 231250, 7000 },
141 { 80, 105250, 7000 },
142 { 79, 0, 0 },
143 { 78, 0, 0 },
144 { 77, 0, 0 },
145 { 74, 69250, 7000 },
146 { 73, 0, 0 },
147 { 70, 45750, 8000 },
148 { 21, 471250, 8000 },
149 { 20, 210250, 8500 },
150 { 18, 192750, 8500 },
151 { 16, 175250, 8000 },
152 { 15, 82250, 8500 },
153 { 13, 53750, 8500 },
154 { 5, 175250, 7000 },
155 { 2, 48250, 7000 } }
156 };
157
158 static const struct cxm_tuner_channels
159 bg_australia_channels = {
160 "B/G Australia",
161 CHNLSET_AUSTRALIA,
162 CXM_TUNER_TV_SYSTEM_BG,
163 1,
164 83,
165 38900,
166 { { 28, 527250, 7000 },
167 { 10, 209250, 7000 },
168 { 6, 175250, 7000 },
169 { 4, 95250, 7000 },
170 { 3, 86250, 7000 },
171 { 1, 57250, 7000 } }
172 };
173
174 static const struct cxm_tuner_channels
175 i_air_channels = {
176 "I Broadcast",
177 0,
178 CXM_TUNER_TV_SYSTEM_I,
179 1,
180 83,
181 38900,
182 { { 75, 179750, 5000 },
183 { 71, 51750, 5000 },
184 { 70, 45000, 5000 },
185 { 21, 471250, 8000 },
186 { 20, 0, 0 },
187 { 19, 0, 0 },
188 { 18, 0, 0 },
189 { 17, 0, 0 },
190 { 16, 0, 0 },
191 { 15, 0, 0 },
192 { 14, 0, 0 },
193 { 13, 247250, 8000 },
194 { 12, 0, 0 },
195 { 4, 175250, 8000 },
196 { 1, 45750, 8000 } }
197 };
198
199 static const struct cxm_tuner_channels
200 l_air_channels = {
201 "L Broadcast",
202 CHNLSET_FRANCE,
203 CXM_TUNER_TV_SYSTEM_L,
204 1,
205 69,
206 38900,
207 { { 21, 471250, 8000 },
208 { 20, 0, 0 },
209 { 19, 0, 0 },
210 { 18, 0, 0 },
211 { 17, 0, 0 },
212 { 16, 0, 0 },
213 { 15, 0, 0 },
214 { 14, 0, 0 },
215 { 8, 176000, 8000 },
216 { 5, 57250, 3250 },
217 { 4, 55750, 1500 },
218 { 3, 54000, 1750 },
219 { 2, 49250, 4750 },
220 { 1, 47750, 1500 } }
221 };
222
223 static const struct cxm_tuner_channels
224 japan_air_channels = {
225 "Japan Broadcast",
226 CHNLSET_JPNBCST,
227 CXM_TUNER_TV_SYSTEM_MN,
228 1,
229 62,
230 45750,
231 { { 13, 471250, 6000 },
232 { 8, 193250, 6000 },
233 { 4, 171250, 6000 },
234 { 1, 91250, 6000 } }
235 };
236
237 static const struct cxm_tuner_channels
238 japan_cable_channels = {
239 "Japan Cable",
240 CHNLSET_JPNCABLE,
241 CXM_TUNER_TV_SYSTEM_MN,
242 1,
243 63,
244 45750,
245 { { 23, 223250, 6000 },
246 { 22, 165250, 6000 },
247 { 13, 109250, 6000 },
248 { 8, 193250, 6000 },
249 { 4, 171250, 6000 },
250 { 1, 91250, 6000 } }
251 };
252
253
254 static const struct cxm_tuner_channels
255 *channel_sets[] = {
256 &us_air_channels,
257 &us_cable_channels,
258 &bg_air_channels,
259 &bg_cable_channels,
260 &bg_australia_channels,
261 &i_air_channels,
262 &l_air_channels,
263 &japan_air_channels,
264 &japan_cable_channels
265 };
266
267
268 const struct cxm_tuner
269 cxm_tuners[CXM_TUNER_TYPES] = {
270 { "Philips FI1216 MK2",
271 { CXM_TUNER_TV_SYSTEM_BG, cxm_none_system_code_style },
272 48250, 855250,
273 { { 450000, { 0x8e, 0x30 } },
274 { 170000, { 0x8e, 0x90 } },
275 { 48250, { 0x8e, 0xa0 } } },
276 0, 0,
277 { 0 },
278 &bg_air_channels },
279 { "Philips FM1216",
280 { CXM_TUNER_TV_SYSTEM_BG, cxm_none_system_code_style },
281 48250, 855250,
282 { { 450000, { 0xce, 0x30 } },
283 { 170000, { 0xce, 0x90 } },
284 { 48250, { 0xce, 0xa0 } } },
285 87500, 108000,
286 { 87500, { 0x88, 0xa5 } },
287 &bg_air_channels },
288 { "Philips FQ1216ME",
289 { CXM_TUNER_TV_SYSTEM_BG | CXM_TUNER_TV_SYSTEM_DK
290 | CXM_TUNER_TV_SYSTEM_I
291 | CXM_TUNER_TV_SYSTEM_L | CXM_TUNER_TV_SYSTEM_L_PRIME,
292 cxm_port_system_code_style,
293 { { CXM_TUNER_TV_SYSTEM_BG, { 0x09 } },
294 { CXM_TUNER_TV_SYSTEM_DK, { 0x09 } },
295 { CXM_TUNER_TV_SYSTEM_I, { 0x01 } },
296 { CXM_TUNER_TV_SYSTEM_L, { 0x0b } },
297 { CXM_TUNER_TV_SYSTEM_L_PRIME, { 0x0a } } } },
298 48250, 855250,
299 { { 450000, { 0x8e, 0x30 } },
300 { 170000, { 0x8e, 0x90 } },
301 { 48250, { 0x8e, 0xa0 } } },
302 0, 0,
303 { 0 },
304 &l_air_channels },
305 { "Philips FQ1216ME MK3",
306 { CXM_TUNER_TV_SYSTEM_BG | CXM_TUNER_TV_SYSTEM_DK
307 | CXM_TUNER_TV_SYSTEM_I
308 | CXM_TUNER_TV_SYSTEM_L | CXM_TUNER_TV_SYSTEM_L_PRIME,
309 cxm_if_system_with_aux_code_style,
310 { { CXM_TUNER_TV_SYSTEM_BG, { 0x16, 0x70, 0x49, 0x40 }},
311 { CXM_TUNER_TV_SYSTEM_DK, { 0x16, 0x70, 0x4b, 0x40 }},
312 { CXM_TUNER_TV_SYSTEM_I, { 0x16, 0x70, 0x4a, 0x40 }},
313 { CXM_TUNER_TV_SYSTEM_L, { 0x06, 0x50, 0x4b, 0x50 }},
314 { CXM_TUNER_TV_SYSTEM_L_PRIME, { 0x86, 0x50, 0x53, 0x50 }}
315 } },
316 48250, 863250,
317 { { 442000, { 0xce, 0x04 } },
318 { 160000, { 0xce, 0x02 } },
319 { 48250, { 0xce, 0x01 } } },
320 0, 0,
321 { 0 },
322 &l_air_channels },
323 { "Philips FM1216ME MK3",
324 { CXM_TUNER_TV_SYSTEM_BG | CXM_TUNER_TV_SYSTEM_DK
325 | CXM_TUNER_TV_SYSTEM_I
326 | CXM_TUNER_TV_SYSTEM_L | CXM_TUNER_TV_SYSTEM_L_PRIME,
327 cxm_if_system_with_aux_code_style,
328 { { CXM_TUNER_TV_SYSTEM_BG, { 0x16, 0x70, 0x49, 0x40 }},
329 { CXM_TUNER_TV_SYSTEM_DK, { 0x16, 0x70, 0x4b, 0x40 }},
330 { CXM_TUNER_TV_SYSTEM_I, { 0x16, 0x70, 0x4a, 0x40 }},
331 { CXM_TUNER_TV_SYSTEM_L, { 0x06, 0x50, 0x4b, 0x50 }},
332 { CXM_TUNER_TV_SYSTEM_L_PRIME, { 0x86, 0x50, 0x53, 0x50 }},
333 { CXM_TUNER_FM_SYSTEM, { 0x0a, 0x90, 0x20, 0x40 }}
334 } },
335 48250, 863250,
336 { { 442000, { 0xce, 0x04 } },
337 { 160000, { 0xce, 0x02 } },
338 { 48250, { 0xce, 0x01 } } },
339 87500, 108000,
340 { 87500, { 0x88, 0x19 } },
341 &l_air_channels },
342 { "Philips FI1236 MK2",
343 { CXM_TUNER_TV_SYSTEM_MN, cxm_none_system_code_style },
344 55250, 801250,
345 { { 454000, { 0x8e, 0x30 } },
346 { 160000, { 0x8e, 0x90 } },
347 { 55250, { 0x8e, 0xa0 } } },
348 0, 0,
349 { 0 },
350 &us_cable_channels },
351 { "Philips FM1236",
352 { CXM_TUNER_TV_SYSTEM_MN, cxm_none_system_code_style },
353 55250, 801250,
354 { { 454000, { 0xce, 0x30 } },
355 { 160000, { 0xce, 0x90 } },
356 { 55250, { 0xce, 0xa0 } } },
357 76000, 108000,
358 { 76000, { 0x88, 0xa5 } },
359 &us_cable_channels },
360 { "Philips FI1246 MK2",
361 { CXM_TUNER_TV_SYSTEM_I, cxm_none_system_code_style },
362 45750, 855250,
363 { { 450000, { 0x8e, 0x30 } },
364 { 170000, { 0x8e, 0x90 } },
365 { 45750, { 0x8e, 0xa0 } } },
366 0, 0,
367 { 0 },
368 &i_air_channels },
369 { "Philips FM1246",
370 { CXM_TUNER_TV_SYSTEM_I, cxm_none_system_code_style },
371 45750, 855250,
372 { { 450000, { 0xce, 0x30 } },
373 { 170000, { 0xce, 0x90 } },
374 { 45750, { 0xce, 0xa0 } } },
375 87500, 108000,
376 { 87500, { 0x88, 0xa5 } },
377 &i_air_channels },
378 { "Temic 4006 FH5",
379 { CXM_TUNER_TV_SYSTEM_BG, cxm_none_system_code_style },
380 48250, 855250,
381 { { 454000, { 0x8e, 0x30 } },
382 { 169000, { 0x8e, 0x90 } },
383 { 48250, { 0x8e, 0xa0 } } },
384 0, 0,
385 { 0 },
386 &bg_air_channels },
387 { "Temic 4009 FR5",
388 { CXM_TUNER_TV_SYSTEM_BG, cxm_none_system_code_style },
389 48250, 855250,
390 { { 464000, { 0x8e, 0x30 } },
391 { 141000, { 0x8e, 0x90 } },
392 { 48250, { 0x8e, 0xa0 } } },
393 87500, 108100,
394 { 87500, { 0x88, 0xa5 } },
395 &bg_air_channels },
396 { "Temic 4036 FY5",
397 { CXM_TUNER_TV_SYSTEM_MN, cxm_none_system_code_style },
398 55250, 801250,
399 { { 453000, { 0x8e, 0x30 } },
400 { 158000, { 0x8e, 0x90 } },
401 { 55250, { 0x8e, 0xa0 } } },
402 0, 0,
403 { 0 },
404 &us_cable_channels },
405 { "Temic 4039 FR5",
406 { CXM_TUNER_TV_SYSTEM_MN, cxm_none_system_code_style },
407 55250, 801250,
408 { { 453000, { 0x8e, 0x30 } },
409 { 158000, { 0x8e, 0x90 } },
410 { 55250, { 0x8e, 0xa0 } } },
411 75900, 108100,
412 { 75900, { 0x88, 0xa5 } },
413 &us_cable_channels },
414 { "Temic 4066 FY5",
415 { CXM_TUNER_TV_SYSTEM_I, cxm_none_system_code_style },
416 45750, 855250,
417 { { 454000, { 0x8e, 0x30 } },
418 { 169000, { 0x8e, 0x90 } },
419 { 45750, { 0x8e, 0xa0 } } },
420 0, 0,
421 { 0 },
422 &i_air_channels },
423 { "LG Innotek TPI8PSB11D",
424 { CXM_TUNER_TV_SYSTEM_BG, cxm_none_system_code_style },
425 48250, 855250,
426 { { 450000, { 0x8e, 0x30 } },
427 { 170000, { 0x8e, 0x90 } },
428 { 48250, { 0x8e, 0xa0 } } },
429 0, 0,
430 { 0 },
431 &bg_air_channels },
432 { "LG Innotek TPI8PSB01N",
433 { CXM_TUNER_TV_SYSTEM_BG, cxm_none_system_code_style },
434 48250, 855250,
435 { { 450000, { 0x8e, 0x30 } },
436 { 170000, { 0x8e, 0x90 } },
437 { 48250, { 0x8e, 0xa0 } } },
438 87500, 108000,
439 { 87500, { 0x88, 0xa5 } },
440 &bg_air_channels },
441 { "LG Innotek TAPC-H701F",
442 { CXM_TUNER_TV_SYSTEM_MN, cxm_none_system_code_style },
443 55250, 801250,
444 { { 450000, { 0xce, 0x08 } },
445 { 165000, { 0xce, 0x02 } },
446 { 55250, { 0xce, 0x01 } } },
447 0, 0,
448 { 0 },
449 &us_cable_channels },
450 { "LG Innotek TAPC-H001F",
451 { CXM_TUNER_TV_SYSTEM_MN, cxm_none_system_code_style },
452 55250, 801250,
453 { { 450000, { 0xce, 0x08 } },
454 { 165000, { 0xce, 0x02 } },
455 { 55250, { 0xce, 0x01 } } },
456 76000, 108000,
457 { 76000, { 0x88, 0x04 } },
458 &us_cable_channels },
459 { "LG Innotek TAPE-H001F",
460 { CXM_TUNER_TV_SYSTEM_MN,
461 cxm_if_system_with_aux_code_style,
462 { { CXM_TUNER_TV_SYSTEM_MN, { 0x16, 0x30, 0x44, 0x30 }},
463 { CXM_TUNER_FM_SYSTEM, { 0x0a, 0x90, 0x20, 0x30 }}
464 } },
465 48250, 801250,
466 { { 442000, { 0xce, 0x04 } },
467 { 160000, { 0xce, 0x02 } },
468 { 48250, { 0xce, 0x01 } } },
469 88000, 108000,
470 { 88000, { 0x88, 0x19 } },
471 &us_cable_channels },
472 { "Microtune 4049 FM5",
473 { CXM_TUNER_TV_SYSTEM_BG | CXM_TUNER_TV_SYSTEM_DK
474 | CXM_TUNER_TV_SYSTEM_I
475 | CXM_TUNER_TV_SYSTEM_L | CXM_TUNER_TV_SYSTEM_L_PRIME,
476 cxm_if_system_code_style,
477 { { CXM_TUNER_TV_SYSTEM_BG, { 0xd4, 0x70, 0x09 } },
478 { CXM_TUNER_TV_SYSTEM_DK, { 0xd4, 0x70, 0x0b } },
479 { CXM_TUNER_TV_SYSTEM_I, { 0xd4, 0x70, 0x0a } },
480 { CXM_TUNER_TV_SYSTEM_L, { 0xc4, 0x10, 0x0b } },
481 { CXM_TUNER_TV_SYSTEM_L_PRIME, { 0x84, 0x10, 0x13 } },
482 { CXM_TUNER_FM_SYSTEM, { 0xdc, 0x10, 0x1d } } } },
483 45750, 855250,
484 { { 464000, { 0x8e, 0x30 } },
485 { 141000, { 0x8e, 0x90 } },
486 { 45750, { 0x8e, 0xa0 } } },
487 87500, 108100,
488 { 87500, { 0x88, 0xa4 } },
489 &l_air_channels },
490 { "TCL 2002N-6A",
491 { CXM_TUNER_TV_SYSTEM_MN, cxm_none_system_code_style },
492 55250, 801250,
493 { { 446000, { 0x8e, 0x08 } },
494 { 170000, { 0x8e, 0x02 } },
495 { 55250, { 0x8e, 0x01 } } },
496 0, 0,
497 { 0 },
498 &us_cable_channels },
499 };
500
501
502 /* Read from the tuner registers */
503 static int
504 cxm_tuner_read(device_t iicbus, int i2c_addr, char *buf, int len)
505 {
506 int received;
507
508 if (iicbus_start(iicbus, i2c_addr + 1, CXM_I2C_TIMEOUT) != 0)
509 return -1;
510
511 if (iicbus_read(iicbus, buf, len, &received, IIC_LAST_READ, 0) != 0)
512 goto fail;
513
514 iicbus_stop(iicbus);
515
516 return received;
517
518 fail:
519 iicbus_stop(iicbus);
520 return -1;
521 }
522
523
524 /* Write to the tuner registers */
525 static int
526 cxm_tuner_write(device_t iicbus, int i2c_addr, const char *buf, int len)
527 {
528 int sent;
529
530 if (iicbus_start(iicbus, i2c_addr, CXM_I2C_TIMEOUT) != 0)
531 return -1;
532
533 if (iicbus_write(iicbus, buf, len, &sent, CXM_I2C_TIMEOUT) != 0)
534 goto fail;
535
536 iicbus_stop(iicbus);
537
538 return sent;
539
540 fail:
541 iicbus_stop(iicbus);
542 return -1;
543 }
544
545
546 int
547 cxm_tuner_init(struct cxm_softc *sc)
548 {
549 unsigned char status;
550 int tuner_type;
551
552 if (cxm_eeprom_init(sc) < 0)
553 return -1;
554
555 tuner_type = cxm_eeprom_tuner_type(sc);
556
557 if (tuner_type < 0 || tuner_type >= NUM_ELEMENTS(cxm_tuners))
558 return -1;
559
560 sc->tuner = &cxm_tuners[tuner_type];
561 sc->tuner_channels = sc->tuner->default_channels;
562 sc->tuner_freq = 0;
563
564 if (cxm_tuner_read(sc->iicbus, CXM_I2C_TUNER, &status, sizeof(status))
565 != sizeof(status))
566 return -1;
567
568 if (cxm_tuner_select_channel(sc, 4) < 0)
569 return -1;
570
571 device_printf(sc->dev, "%s tuner\n", sc->tuner->name);
572
573 return 0;
574 }
575
576
577 int
578 cxm_tuner_select_channel_set(struct cxm_softc *sc, unsigned int channel_set)
579 {
580 unsigned int i;
581
582 if (!channel_set) {
583 sc->tuner_channels = sc->tuner->default_channels;
584 return 0;
585 }
586
587 for (i = 0; i < NUM_ELEMENTS(channel_sets); i++)
588 if (channel_sets[i]->chnlset == channel_set)
589 break;
590
591 if (i >= NUM_ELEMENTS(channel_sets))
592 return -1;
593
594 if (!(sc->tuner->systems.supported & channel_sets[i]->system))
595 return -1;
596
597 sc->tuner_channels = channel_sets[i];
598
599 return 0;
600 }
601
602
603 unsigned int
604 cxm_tuner_selected_channel_set(struct cxm_softc *sc)
605 {
606 return sc->tuner_channels->chnlset;
607 }
608
609
610 int
611 cxm_tuner_select_frequency(struct cxm_softc *sc,
612 enum cxm_tuner_freq_type freq_type,
613 unsigned long freq)
614 {
615 unsigned char msg[5];
616 unsigned char aux;
617 unsigned char pb;
618 unsigned int i;
619 unsigned int system;
620 unsigned int tuner_msg_len;
621 unsigned long N;
622 unsigned long osc_freq;
623 const struct cxm_tuner_band_code *band_code;
624 const struct cxm_tuner_system_code *system_code;
625
626 N = 0;
627 aux = 0;
628 pb = 0;
629
630 system_code = NULL;
631
632 tuner_msg_len = 4;
633
634 if (sc->tuner->systems.code_style != cxm_none_system_code_style) {
635 system = freq_type == cxm_tuner_fm_freq_type
636 ? CXM_TUNER_FM_SYSTEM : sc->tuner_channels->system;
637
638 for (i = 0; i < NUM_ELEMENTS (sc->tuner->systems.codes); i++)
639 if (sc->tuner->systems.codes[i].system & system)
640 break;
641
642 if (i >= NUM_ELEMENTS (sc->tuner->systems.codes))
643 return -1;
644
645 switch (sc->tuner->systems.code_style) {
646 case cxm_port_system_code_style:
647 pb = sc->tuner->systems.codes[i].codes[0];
648 break;
649
650 case cxm_if_system_with_aux_code_style:
651 aux = sc->tuner->systems.codes[i].codes[3];
652 tuner_msg_len = 5;
653
654 /* FALLTHROUGH */
655
656 case cxm_if_system_code_style:
657 system_code = &sc->tuner->systems.codes[i];
658 break;
659
660 default:
661 return -1;
662 }
663 }
664
665 switch (freq_type) {
666 case cxm_tuner_fm_freq_type:
667
668 if (freq < sc->tuner->fm_min_freq
669 || freq > sc->tuner->fm_max_freq
670 || !sc->tuner->fm_band_code.freq)
671 return -1;
672
673 /*
674 * The Philips FM1216ME MK3 data sheet recommends
675 * first setting the tuner to TV mode at a high
676 * frequency (e.g. 150 MHz), then selecting the
677 * desired FM station in order to ensure that the
678 * tuning voltage does not stay locked at 0V.
679 */
680
681 if (cxm_tuner_select_frequency(sc, cxm_tuner_tv_freq_type,
682 150000) < 0)
683 return -1;
684
685 /*
686 * N = { fRF(pc) + fIF(pc) } / step_size
687 *
688 * fRF = RF frequency in MHz
689 * fIF = Intermediate frequency in MHz (FM = 10.70 MHz)
690 * step_size = Step size in MHz (FM = 50 kHz)
691 */
692
693 osc_freq = freq + 10700;
694
695 N = (20 * osc_freq) / 1000;
696
697 msg[0] = (unsigned char)(N >> 8);
698 msg[1] = (unsigned char)N;
699 msg[2] = sc->tuner->fm_band_code.codes[0];
700 msg[3] = sc->tuner->fm_band_code.codes[1] | pb;
701 msg[4] = aux;
702 break;
703
704 case cxm_tuner_tv_freq_type:
705
706 if (freq < sc->tuner->min_freq
707 || freq > sc->tuner->max_freq)
708 return -1;
709
710 /*
711 * N = 16 * { fRF(pc) + fIF(pc) }
712 *
713 * fRF = RF frequency in MHz
714 * fIF = Intermediate frequency in MHz
715 *
716 * The data sheet doesn't state it, however
717 * this is probably the same equation as
718 * FM simply with 62.5 kHz as the step size.
719 */
720
721 osc_freq = freq + sc->tuner_channels->if_freq;
722
723 N = (16 * osc_freq) / 1000;
724
725 for (band_code = sc->tuner->band_codes;
726 band_code->freq > freq; band_code++)
727 ;
728
729 if (freq >= sc->tuner_freq) {
730 msg[0] = (unsigned char)(N >> 8);
731 msg[1] = (unsigned char)N;
732 msg[2] = band_code->codes[0];
733 msg[3] = band_code->codes[1] | pb;
734 } else {
735 msg[0] = band_code->codes[0];
736 msg[1] = band_code->codes[1] | pb;
737 msg[2] = (unsigned char)(N >> 8);
738 msg[3] = (unsigned char)N;
739 }
740 msg[4] = aux;
741 break;
742
743 default:
744 return -1;
745 }
746
747 if (N > 32767)
748 return -1;
749
750 if (cxm_tuner_write(sc->iicbus, CXM_I2C_TUNER, msg, tuner_msg_len)
751 != tuner_msg_len)
752 return -1;
753
754 /*
755 * Program the IF processing after the tuner since some tuners
756 * use the control byte to set the address of the IF.
757 */
758
759 if (system_code) {
760 msg[0] = 0x00;
761 msg[1] = system_code->codes[0];
762 msg[2] = system_code->codes[1];
763 msg[3] = system_code->codes[2];
764
765 if (cxm_tuner_write(sc->iicbus, CXM_I2C_TUNER_IF, msg, 4) != 4)
766 return -1;
767 }
768
769 sc->tuner_freq = freq;
770
771 return 0;
772 }
773
774
775 int
776 cxm_tuner_select_channel(struct cxm_softc *sc, unsigned int channel)
777 {
778 unsigned long freq;
779 const struct cxm_tuner_channel_assignment *assignments;
780 const struct cxm_tuner_channels *channels;
781
782 channels = sc->tuner_channels;
783
784 if (!channels
785 || channel < channels->min_channel
786 || channel > channels->max_channel)
787 return -1;
788
789 for (assignments = channels->assignments;
790 assignments->channel > channel; assignments++)
791 ;
792
793 if (!assignments->freq)
794 return -1;
795
796 freq = assignments->freq
797 + (channel - assignments->channel) * assignments->step;
798
799 return cxm_tuner_select_frequency(sc, cxm_tuner_tv_freq_type, freq);
800 }
801
802
803 int
804 cxm_tuner_apply_afc(struct cxm_softc *sc)
805 {
806 unsigned int i;
807 int status;
808 unsigned long freq;
809 unsigned long max_offset;
810 unsigned long original_freq;
811 unsigned long prev_freq;
812 unsigned long step_size;
813
814 if (cxm_tuner_wait_for_lock(sc) != 1)
815 return -1;
816
817 original_freq = sc->tuner_freq;
818
819 freq = sc->tuner_freq;
820 prev_freq = 0;
821 max_offset = 2000;
822 step_size = 63;
823
824 for (i = 0; i < (max_offset / step_size); i++) {
825 status = cxm_tuner_status(sc);
826
827 if (status == -1)
828 break;
829
830 if (!(status & CXM_TUNER_PHASE_LOCKED))
831 break;
832
833 switch (status & CXM_TUNER_AFC_MASK) {
834 case CXM_TUNER_AFC_FREQ_CENTERED:
835 return 0;
836
837 case CXM_TUNER_AFC_FREQ_MINUS_125:
838 case CXM_TUNER_AFC_FREQ_MINUS_62:
839 freq -= step_size;
840 break;
841
842 case CXM_TUNER_AFC_FREQ_PLUS_62:
843 case CXM_TUNER_AFC_FREQ_PLUS_125:
844 freq += step_size;
845 break;
846
847 default:
848 goto fail;
849 }
850
851 if (freq == prev_freq)
852 return 0;
853 prev_freq = sc->tuner_freq;
854
855 if (cxm_tuner_select_frequency(sc, cxm_tuner_tv_freq_type,
856 freq) < 0)
857 break;
858
859 /*
860 * Delay long enough for the tuner to update its status.
861 */
862
863 tsleep(&sc->iicbus, 0, "afc", hz / 10);
864 }
865
866 fail:
867 cxm_tuner_select_frequency(sc, cxm_tuner_tv_freq_type, original_freq);
868 return -1;
869 }
870
871
872 int
873 cxm_tuner_is_locked(struct cxm_softc *sc)
874 {
875 int status;
876
877 status = cxm_tuner_status(sc);
878
879 if (status == -1)
880 return -1;
881
882 return (status & CXM_TUNER_PHASE_LOCKED) ? 1 : 0;
883 }
884
885
886 int
887 cxm_tuner_wait_for_lock(struct cxm_softc *sc)
888 {
889 unsigned int i;
890
891 /*
892 * The data sheet states the maximum lock-in time
893 * is 150 ms using fast tuning ... unfortunately
894 * it doesn't state the maximum lock-in time using
895 * moderate tuning. Hopefully 300 ms is enough.
896 */
897
898 for (i = 0; i < 3; i++) {
899
900 /*
901 * The frequency may have just changed (prior to
902 * cxm_tuner_wait_for_lock) so start with the delay
903 * to give the tuner a chance to update its status.
904 */
905
906 tsleep(&sc->iicbus, 0, "tuner", hz / 10);
907
908 switch (cxm_tuner_is_locked(sc)) {
909 case 1:
910 return 1;
911
912 case 0:
913 break;
914
915 default:
916 return -1;
917 }
918 }
919
920 device_printf(sc->dev, "tuner failed to lock\n");
921
922 return 0;
923 }
924
925
926 static const unsigned char afcmap[16] = {
927 CXM_TUNER_AFC_FREQ_CENTERED,
928 CXM_TUNER_AFC_FREQ_MINUS_62,
929 CXM_TUNER_AFC_FREQ_MINUS_62,
930 CXM_TUNER_AFC_FREQ_MINUS_62,
931 CXM_TUNER_AFC_FREQ_MINUS_125,
932 CXM_TUNER_AFC_FREQ_MINUS_125,
933 CXM_TUNER_AFC_FREQ_MINUS_125,
934 CXM_TUNER_AFC_FREQ_MINUS_125,
935 CXM_TUNER_AFC_FREQ_PLUS_125,
936 CXM_TUNER_AFC_FREQ_PLUS_125,
937 CXM_TUNER_AFC_FREQ_PLUS_125,
938 CXM_TUNER_AFC_FREQ_PLUS_125,
939 CXM_TUNER_AFC_FREQ_PLUS_62,
940 CXM_TUNER_AFC_FREQ_PLUS_62,
941 CXM_TUNER_AFC_FREQ_PLUS_62,
942 CXM_TUNER_AFC_FREQ_CENTERED
943 };
944
945 int
946 cxm_tuner_status(struct cxm_softc *sc)
947 {
948 unsigned char status;
949
950 if (cxm_tuner_read(sc->iicbus, CXM_I2C_TUNER, &status, sizeof(status))
951 != sizeof(status))
952 return -1;
953
954 if (sc->tuner->systems.code_style == cxm_if_system_code_style
955 || sc->tuner->systems.code_style
956 == cxm_if_system_with_aux_code_style) {
957 unsigned char if_status;
958
959 if (cxm_tuner_read(sc->iicbus, CXM_I2C_TUNER_IF,
960 &if_status, sizeof(if_status))
961 != sizeof(if_status))
962 return -1;
963
964 status &= ~CXM_TUNER_AFC_MASK;
965 status |= afcmap[(if_status >> 1) & 0x0f];
966 }
967
968 return status;
969 }
Cache object: aaa8ae46b9ee373e64991c41b090faf0
|