xref: /dragonfly/sys/dev/video/cxm/cxm_tuner.c (revision 1975d09e)
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 }
970