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 * Audio decoder 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 audio decoder interface so that 37 * it's not necessary for each multimedia driver to re-invent 38 * the 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/cxm/cxm.h> 55 56 #include <bus/iicbus/iiconf.h> 57 #include <bus/iicbus/iicbus.h> 58 59 #include "iicbb_if.h" 60 61 62 static const struct cxm_msp_command 63 msp34x5G_init = { 64 5, 65 { 66 /* Enable Automatic Sound Select */ 67 { CXM_MSP3400C_DEM, 0x0030, { 0x20, 0x03 } }, 68 /* SCART Prescale = 0 dB */ 69 { CXM_MSP3400C_DFP, 0x000d, { 0x19, 0x00 } }, 70 /* FM / AM Prescale = 100 Khz and FM Matrix = Sound A Mono */ 71 { CXM_MSP3400C_DFP, 0x000e, { 0x24, 0x03 } }, 72 /* NICAM Prescale = 9 dB */ 73 { CXM_MSP3400C_DFP, 0x0010, { 0x5a, 0x00 } }, 74 /* Enable Automatic Standard Select */ 75 { CXM_MSP3400C_DEM, 0x0020, { 0x00, 0x01 } }, 76 } 77 }; 78 79 static const struct cxm_msp_command 80 msp34x5G_select_tuner = { 81 3, 82 { 83 /* Loudspeaker Source = demodulator (St or A), Matrix = St */ 84 { CXM_MSP3400C_DFP, 0x0008, { 0x03, 0x20 } }, 85 /* SCART1_L/R Source = demodulator (St or A), Matrix = St */ 86 { CXM_MSP3400C_DFP, 0x000a, { 0x03, 0x20 } }, 87 /* DSP In = mute, SC1_OUT_L/R Source = SCART1_L/R */ 88 { CXM_MSP3400C_DFP, 0x0013, { 0x0f, 0x20 } } 89 } 90 }; 91 92 static const struct cxm_msp_command 93 msp34x5D_init = { 94 4, 95 { 96 /* Enable Automatic NICAM-FM/AM Switching */ 97 { CXM_MSP3400C_DEM, 0x0021, { 0x00, 0x01 } }, 98 /* SCART Prescale = 0 dB */ 99 { CXM_MSP3400C_DFP, 0x000d, { 0x19, 0x00 } }, 100 /* NICAM Prescale = 9 dB */ 101 { CXM_MSP3400C_DFP, 0x0010, { 0x5a, 0x00 } }, 102 /* Enable Automatic Standard Select */ 103 { CXM_MSP3400C_DEM, 0x0020, { 0x00, 0x01 } }, 104 } 105 }; 106 107 static const struct cxm_msp_command 108 msp34x5D_select_tuner = { 109 5, 110 { 111 /* Loudspeaker Source = demodulator (NICAM), Matrix = St */ 112 { CXM_MSP3400C_DFP, 0x0008, { 0x01, 0x20 } }, 113 /* SCART1_L/R Source = demodulator (NICAM), Matrix = St */ 114 { CXM_MSP3400C_DFP, 0x000a, { 0x01, 0x20 } }, 115 /* FM / AM Prescale = 100 Khz and FM Matrix = No Matrix */ 116 { CXM_MSP3400C_DFP, 0x000e, { 0x24, 0x00 } }, 117 /* FM Deemphasis = 50 us */ 118 { CXM_MSP3400C_DFP, 0x000f, { 0x00, 0x00 } }, 119 /* DSP In = mute, SC1_OUT_L/R Source = SCART1_L/R */ 120 { CXM_MSP3400C_DFP, 0x0013, { 0x0f, 0x20 } } 121 } 122 }; 123 124 static const struct cxm_msp_command 125 msp34xxx_mute = { 126 2, 127 { 128 /* Loudspeaker volume = mute */ 129 { CXM_MSP3400C_DFP, 0x0000, { 0x00, 0x00 } }, 130 /* SC1_OUT_L/R volume = mute */ 131 { CXM_MSP3400C_DFP, 0x0007, { 0x00, 0x01 } } 132 } 133 }; 134 135 static const struct cxm_msp_command 136 msp34xxx_unmute = { 137 2, 138 { 139 /* Loudspeaker volume = 0 db */ 140 { CXM_MSP3400C_DFP, 0x0000, { 0x73, 0x00 } }, 141 /* SC1_OUT_L/R volume = 0 db */ 142 { CXM_MSP3400C_DFP, 0x0007, { 0x73, 0x01 } } 143 } 144 }; 145 146 static const struct cxm_msp_command 147 msp34xxx_select_fm = { 148 3, 149 { 150 /* Loudspeaker Source = SCART, Matrix = STEREO */ 151 { CXM_MSP3400C_DFP, 0x0008, { 0x02, 0x20 } }, 152 /* SCART1_L/R Source = SCART, Matrix = STEREO */ 153 { CXM_MSP3400C_DFP, 0x000a, { 0x02, 0x20 } }, 154 /* DSP In = SC2_IN_L/R, SC1_OUT_L/R Source = SCART1_L/R */ 155 { CXM_MSP3400C_DFP, 0x0013, { 0x0e, 0x00 } } 156 } 157 }; 158 159 static const struct cxm_msp_command 160 msp34xxx_select_line_in = { 161 3, 162 { 163 /* Loudspeaker Source = SCART, Matrix = STEREO */ 164 { CXM_MSP3400C_DFP, 0x0008, { 0x02, 0x20 } }, 165 /* SCART1_L/R Source = SCART, Matrix = STEREO */ 166 { CXM_MSP3400C_DFP, 0x000a, { 0x02, 0x20 } }, 167 /* DSP In = SC1_IN_L/R, SC1_OUT_L/R Source = SCART1_L/R */ 168 { CXM_MSP3400C_DFP, 0x0013, { 0x0c, 0x00 } } 169 } 170 }; 171 172 173 /* Reset the MSP or DPL chip */ 174 static int 175 cxm_msp_dpl_reset(device_t iicbus, int i2c_addr) 176 { 177 unsigned char msg[3]; 178 int sent; 179 180 /* put into reset mode */ 181 msg[0] = 0x00; 182 msg[1] = 0x80; 183 msg[2] = 0x00; 184 185 if (iicbus_start(iicbus, i2c_addr, CXM_I2C_TIMEOUT) != 0) 186 return -1; 187 188 if (iicbus_write(iicbus, msg, sizeof(msg), &sent, CXM_I2C_TIMEOUT) != 0 189 || sent != sizeof(msg)) 190 goto fail; 191 192 iicbus_stop(iicbus); 193 194 /* put back to operational mode */ 195 msg[0] = 0x00; 196 msg[1] = 0x00; 197 msg[2] = 0x00; 198 199 if (iicbus_start(iicbus, i2c_addr, CXM_I2C_TIMEOUT) != 0) 200 return -1; 201 202 if (iicbus_write(iicbus, msg, sizeof(msg), &sent, CXM_I2C_TIMEOUT) != 0 203 || sent != sizeof(msg)) 204 goto fail; 205 206 iicbus_stop(iicbus); 207 208 return 0; 209 210 fail: 211 iicbus_stop(iicbus); 212 return -1; 213 } 214 215 216 /* Read from the MSP or DPL registers */ 217 static int 218 cxm_msp_dpl_read(device_t iicbus, int i2c_addr, 219 unsigned char dev, unsigned int addr, 220 char *buf, int len) 221 { 222 unsigned char msg[3]; 223 int received; 224 int sent; 225 226 msg[0] = (unsigned char)(dev + 1); 227 msg[1] = (unsigned char)(addr >> 8); 228 msg[2] = (unsigned char)addr; 229 230 if (iicbus_start(iicbus, i2c_addr, CXM_I2C_TIMEOUT) != 0) 231 return -1; 232 233 if (iicbus_write(iicbus, msg, sizeof(msg), &sent, CXM_I2C_TIMEOUT) != 0 234 || sent != sizeof(msg)) 235 goto fail; 236 237 if (iicbus_repeated_start(iicbus, i2c_addr + 1, CXM_I2C_TIMEOUT) != 0) 238 goto fail; 239 240 if (iicbus_read(iicbus, buf, len, &received, IIC_LAST_READ, 0) != 0) 241 goto fail; 242 243 iicbus_stop(iicbus); 244 245 return received; 246 247 fail: 248 iicbus_stop(iicbus); 249 return -1; 250 } 251 252 253 /* Write to the MSP or DPL registers */ 254 static int 255 cxm_msp_dpl_write(device_t iicbus, int i2c_addr, 256 unsigned char dev, unsigned int addr, 257 const char *buf, int len) 258 { 259 unsigned char msg[3]; 260 int sent; 261 262 msg[0] = dev; 263 msg[1] = (unsigned char)(addr >> 8); 264 msg[2] = (unsigned char)addr; 265 266 if (iicbus_start(iicbus, i2c_addr, CXM_I2C_TIMEOUT) != 0) 267 return -1; 268 269 if (iicbus_write(iicbus, msg, sizeof(msg), &sent, CXM_I2C_TIMEOUT) != 0 270 || sent != sizeof(msg)) 271 goto fail; 272 273 if (iicbus_write(iicbus, buf, len, &sent, CXM_I2C_TIMEOUT) != 0) 274 goto fail; 275 276 iicbus_stop(iicbus); 277 278 return sent; 279 280 fail: 281 iicbus_stop(iicbus); 282 return -1; 283 } 284 285 286 int 287 cxm_msp_init(struct cxm_softc *sc) 288 { 289 unsigned char rev1[2]; 290 unsigned char rev2[2]; 291 unsigned int i; 292 unsigned int nsettings; 293 const struct cxm_msp_setting *settings; 294 295 if (cxm_msp_dpl_reset (sc->iicbus, CXM_I2C_MSP3400) < 0) 296 return -1; 297 298 if (cxm_msp_dpl_read(sc->iicbus, CXM_I2C_MSP3400, CXM_MSP3400C_DFP, 299 0x001e, rev1, sizeof(rev1)) != sizeof(rev1)) 300 return -1; 301 302 if (cxm_msp_dpl_read(sc->iicbus, CXM_I2C_MSP3400, CXM_MSP3400C_DFP, 303 0x001f, rev2, sizeof(rev2)) != sizeof(rev2)) 304 return -1; 305 306 ksnprintf(sc->msp_name, sizeof(sc->msp_name), "%c4%02d%c-%c%d", 307 ((rev1[1] >> 4) & 0x0f) + '3', rev2[0], 308 (rev1[1] & 0x0f) + '@', rev1[0] + '@', rev2[1] & 0x1f); 309 310 /* 311 * MSP 34x5D, 34x5G, and MSP 44x8G are the 312 * only audio decoders currently supported. 313 */ 314 315 if (strncmp(&sc->msp_name[0], "34", 2) == 0 316 && strncmp(&sc->msp_name[3], "5D", 2) == 0) 317 ; 318 else if (strncmp(&sc->msp_name[0], "34", 2) == 0 319 && strncmp(&sc->msp_name[3], "5G", 2) == 0) 320 ; 321 else if (strncmp(&sc->msp_name[0], "44", 2) == 0 322 && strncmp(&sc->msp_name[3], "8G", 2) == 0) 323 ; 324 else { 325 device_printf(sc->dev, "unknown audio decoder MSP%s\n", 326 sc->msp_name); 327 return -1; 328 } 329 330 nsettings = msp34x5G_init.nsettings; 331 settings = msp34x5G_init.settings; 332 if (sc->msp_name[4] == 'D') { 333 nsettings = msp34x5D_init.nsettings; 334 settings = msp34x5D_init.settings; 335 } 336 337 for (i = 0; i < nsettings; i++) 338 if (cxm_msp_dpl_write(sc->iicbus, CXM_I2C_MSP3400, 339 settings[i].dev, settings[i].addr, 340 settings[i].value, 341 sizeof(settings[i].value)) 342 != sizeof(settings[i].value)) 343 return -1; 344 345 if (cxm_msp_select_source(sc, cxm_tuner_source) < 0) 346 return -1; 347 348 device_printf(sc->dev, "MSP%s audio decoder\n", sc->msp_name); 349 350 return 0; 351 } 352 353 354 int 355 cxm_msp_mute(struct cxm_softc *sc) 356 { 357 unsigned int i; 358 unsigned int nsettings; 359 const struct cxm_msp_setting *settings; 360 361 nsettings = msp34xxx_mute.nsettings; 362 settings = msp34xxx_mute.settings; 363 364 for (i = 0; i < nsettings; i++) 365 if (cxm_msp_dpl_write(sc->iicbus, CXM_I2C_MSP3400, 366 settings[i].dev, settings[i].addr, 367 settings[i].value, 368 sizeof(settings[i].value)) 369 != sizeof(settings[i].value)) 370 return -1; 371 372 return 0; 373 } 374 375 376 int 377 cxm_msp_unmute(struct cxm_softc *sc) 378 { 379 unsigned int i; 380 unsigned int nsettings; 381 const struct cxm_msp_setting *settings; 382 383 nsettings = msp34xxx_unmute.nsettings; 384 settings = msp34xxx_unmute.settings; 385 386 for (i = 0; i < nsettings; i++) 387 if (cxm_msp_dpl_write(sc->iicbus, CXM_I2C_MSP3400, 388 settings[i].dev, settings[i].addr, 389 settings[i].value, 390 sizeof(settings[i].value)) 391 != sizeof(settings[i].value)) 392 return -1; 393 394 return 0; 395 } 396 397 398 int 399 cxm_msp_is_muted(struct cxm_softc *sc) 400 { 401 unsigned char volume[2]; 402 403 if (cxm_msp_dpl_read(sc->iicbus, CXM_I2C_MSP3400, CXM_MSP3400C_DFP, 404 0x0000, volume, sizeof(volume)) != sizeof(volume)) 405 return -1; 406 407 return volume[0] == 0x00 || volume[0] == 0xff ? 1 : 0; 408 } 409 410 411 int 412 cxm_msp_select_source(struct cxm_softc *sc, enum cxm_source source) 413 { 414 unsigned int i; 415 unsigned int nsettings; 416 const struct cxm_msp_setting *settings; 417 418 switch (source) { 419 case cxm_fm_source: 420 nsettings = msp34xxx_select_fm.nsettings; 421 settings = msp34xxx_select_fm.settings; 422 break; 423 424 case cxm_line_in_source_composite: 425 case cxm_line_in_source_svideo: 426 nsettings = msp34xxx_select_line_in.nsettings; 427 settings = msp34xxx_select_line_in.settings; 428 break; 429 430 case cxm_tuner_source: 431 nsettings = msp34x5G_select_tuner.nsettings; 432 settings = msp34x5G_select_tuner.settings; 433 if (sc->msp_name[4] == 'D') { 434 nsettings = msp34x5D_select_tuner.nsettings; 435 settings = msp34x5D_select_tuner.settings; 436 } 437 break; 438 439 default: 440 return -1; 441 } 442 443 for (i = 0; i < nsettings; i++) 444 if (cxm_msp_dpl_write(sc->iicbus, CXM_I2C_MSP3400, 445 settings[i].dev, settings[i].addr, 446 settings[i].value, 447 sizeof(settings[i].value)) 448 != sizeof(settings[i].value)) 449 return -1; 450 451 return 0; 452 } 453 454 455 enum cxm_source 456 cxm_msp_selected_source(struct cxm_softc *sc) 457 { 458 unsigned char dsp[2]; 459 unsigned char source[2]; 460 461 if (cxm_msp_dpl_read(sc->iicbus, CXM_I2C_MSP3400, CXM_MSP3400C_DFP, 462 0x0008, source, sizeof(source)) != sizeof(source)) 463 return cxm_unknown_source; 464 465 switch (source[0]) { 466 case 0: /* FM / AM mono signal */ 467 case 1: /* Stereo or A / B */ 468 case 3: /* Stereo or A */ 469 case 4: /* Stereo or B */ 470 return cxm_tuner_source; 471 472 case 2: /* SCART */ 473 break; 474 475 default: 476 return cxm_unknown_source; 477 } 478 479 if (cxm_msp_dpl_read(sc->iicbus, CXM_I2C_MSP3400, CXM_MSP3400C_DFP, 480 0x0013, dsp, sizeof(dsp)) != sizeof(dsp)) 481 return cxm_unknown_source; 482 483 if (dsp[1] & 0x20) 484 return cxm_unknown_source; 485 486 switch (dsp[0] & 0x03) { 487 case 0: 488 return cxm_line_in_source_composite; 489 490 case 2: 491 return cxm_fm_source; 492 493 default: 494 return cxm_unknown_source; 495 } 496 } 497 498 499 int 500 cxm_msp_autodetect_standard(struct cxm_softc *sc) 501 { 502 unsigned int i; 503 int locked; 504 unsigned int nsettings; 505 const struct cxm_msp_setting *settings; 506 507 switch (cxm_msp_selected_source(sc)) { 508 case cxm_tuner_source: 509 break; 510 511 case cxm_fm_source: 512 case cxm_line_in_source_composite: 513 case cxm_line_in_source_svideo: 514 return 1; 515 516 default: 517 return -1; 518 } 519 520 /* 521 * Section 3.3.2.2 of the data sheet states: 522 * 523 * A general refresh of the STANDARD SELECT 524 * register is not allowed. 525 */ 526 527 if (cxm_msp_dpl_reset (sc->iicbus, CXM_I2C_MSP3400) < 0) 528 return -1; 529 530 nsettings = msp34x5G_init.nsettings; 531 settings = msp34x5G_init.settings; 532 if (sc->msp_name[4] == 'D') { 533 nsettings = msp34x5D_init.nsettings; 534 settings = msp34x5D_init.settings; 535 } 536 537 for (i = 0; i < nsettings; i++) 538 if (cxm_msp_dpl_write(sc->iicbus, CXM_I2C_MSP3400, 539 settings[i].dev, settings[i].addr, 540 settings[i].value, 541 sizeof(settings[i].value)) 542 != sizeof(settings[i].value)) 543 return -1; 544 545 locked = cxm_msp_wait_for_lock(sc); 546 547 if (cxm_msp_select_source(sc, cxm_tuner_source) < 0) 548 return -1; 549 550 return locked; 551 } 552 553 554 int 555 cxm_msp_is_locked(struct cxm_softc *sc) 556 { 557 unsigned char source[2]; 558 unsigned char standard[2]; 559 560 if (cxm_msp_dpl_read(sc->iicbus, CXM_I2C_MSP3400, CXM_MSP3400C_DFP, 561 0x0008, source, sizeof(source)) != sizeof(source)) 562 return -1; 563 564 switch (source[0]) { 565 case 0: /* FM / AM mono signal */ 566 case 1: /* Stereo or A / B */ 567 case 3: /* Stereo or A */ 568 case 4: /* Stereo or B */ 569 break; 570 571 default: 572 return 1; 573 } 574 575 if (cxm_msp_dpl_read(sc->iicbus, CXM_I2C_MSP3400, CXM_MSP3400C_DEM, 576 0x007e, standard, sizeof(standard)) 577 != sizeof(standard)) 578 return -1; 579 580 if (standard[0] >= 8 || (standard[0] == 0 && standard[1] == 0)) 581 return 0; 582 583 return 1; 584 } 585 586 587 int 588 cxm_msp_wait_for_lock(struct cxm_softc *sc) 589 { 590 unsigned int i; 591 592 /* 593 * Section 3.3.2.1 of the data sheet states: 594 * 595 * Within 0.5 s the detection and setup of the actual 596 * TV sound standard is performed. The detected result 597 * can be read out of the STANDARD RESULT register by 598 * the control processor. 599 */ 600 601 for (i = 0; i < 10; i++) { 602 603 /* 604 * The input may have just changed (prior to 605 * cxm_msp_wait_for_lock) so start with the 606 * delay to give the audio decoder a chance 607 * to update its status. 608 */ 609 610 tsleep(&sc->iicbus, 0, "audio", hz / 20); 611 612 switch (cxm_msp_is_locked(sc)) { 613 case 1: 614 return 1; 615 616 case 0: 617 break; 618 619 default: 620 return -1; 621 } 622 } 623 624 device_printf(sc->dev, "audio decoder failed to lock\n"); 625 626 return 0; 627 } 628