Initial import of binutils 2.22 on the new vendor branch
[dragonfly.git] / sys / dev / video / cxm / cxm_tuner.c
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 }