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