xref: /dragonfly/sys/dev/video/cxm/cxm_tuner.c (revision 86d7f5d305c6adaa56ff4582ece9859d73106103)
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
cxm_tuner_read(device_t iicbus,int i2c_addr,char * buf,int len)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
cxm_tuner_write(device_t iicbus,int i2c_addr,const char * buf,int len)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
cxm_tuner_init(struct cxm_softc * sc)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
cxm_tuner_select_channel_set(struct cxm_softc * sc,unsigned int channel_set)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
cxm_tuner_selected_channel_set(struct cxm_softc * sc)604 cxm_tuner_selected_channel_set(struct cxm_softc *sc)
605 {
606           return sc->tuner_channels->chnlset;
607 }
608 
609 
610 int
cxm_tuner_select_frequency(struct cxm_softc * sc,enum cxm_tuner_freq_type freq_type,unsigned long freq)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
cxm_tuner_select_channel(struct cxm_softc * sc,unsigned int channel)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
cxm_tuner_apply_afc(struct cxm_softc * sc)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
cxm_tuner_is_locked(struct cxm_softc * sc)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
cxm_tuner_wait_for_lock(struct cxm_softc * sc)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
cxm_tuner_status(struct cxm_softc * sc)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 }
970