2 * Copyright (c) 2003, 2004, 2005
3 * John Wehle <john@feith.com>. All rights reserved.
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
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.
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.
33 * Audio decoder routines for the Conexant MPEG-2 Codec driver.
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
41 #include <sys/param.h>
42 #include <sys/systm.h>
45 #include <sys/kernel.h>
47 #include <sys/select.h>
48 #include <sys/resource.h>
52 #include <machine/clock.h>
54 #include <dev/video/cxm/cxm.h>
56 #include <bus/iicbus/iiconf.h>
57 #include <bus/iicbus/iicbus.h>
62 static const struct cxm_msp_command
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 } },
79 static const struct cxm_msp_command
80 msp34x5G_select_tuner = {
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 } }
92 static const struct cxm_msp_command
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 } },
107 static const struct cxm_msp_command
108 msp34x5D_select_tuner = {
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 } }
124 static const struct cxm_msp_command
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 } }
135 static const struct cxm_msp_command
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 } }
146 static const struct cxm_msp_command
147 msp34xxx_select_fm = {
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 } }
159 static const struct cxm_msp_command
160 msp34xxx_select_line_in = {
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 } }
173 /* Reset the MSP or DPL chip */
175 cxm_msp_dpl_reset(device_t iicbus, int i2c_addr)
177 unsigned char msg[3];
180 /* put into reset mode */
185 if (iicbus_start(iicbus, i2c_addr, CXM_I2C_TIMEOUT) != 0)
188 if (iicbus_write(iicbus, msg, sizeof(msg), &sent, CXM_I2C_TIMEOUT) != 0
189 || sent != sizeof(msg))
194 /* put back to operational mode */
199 if (iicbus_start(iicbus, i2c_addr, CXM_I2C_TIMEOUT) != 0)
202 if (iicbus_write(iicbus, msg, sizeof(msg), &sent, CXM_I2C_TIMEOUT) != 0
203 || sent != sizeof(msg))
216 /* Read from the MSP or DPL registers */
218 cxm_msp_dpl_read(device_t iicbus, int i2c_addr,
219 unsigned char dev, unsigned int addr,
222 unsigned char msg[3];
226 msg[0] = (unsigned char)(dev + 1);
227 msg[1] = (unsigned char)(addr >> 8);
228 msg[2] = (unsigned char)addr;
230 if (iicbus_start(iicbus, i2c_addr, CXM_I2C_TIMEOUT) != 0)
233 if (iicbus_write(iicbus, msg, sizeof(msg), &sent, CXM_I2C_TIMEOUT) != 0
234 || sent != sizeof(msg))
237 if (iicbus_repeated_start(iicbus, i2c_addr + 1, CXM_I2C_TIMEOUT) != 0)
240 if (iicbus_read(iicbus, buf, len, &received, IIC_LAST_READ, 0) != 0)
253 /* Write to the MSP or DPL registers */
255 cxm_msp_dpl_write(device_t iicbus, int i2c_addr,
256 unsigned char dev, unsigned int addr,
257 const char *buf, int len)
259 unsigned char msg[3];
263 msg[1] = (unsigned char)(addr >> 8);
264 msg[2] = (unsigned char)addr;
266 if (iicbus_start(iicbus, i2c_addr, CXM_I2C_TIMEOUT) != 0)
269 if (iicbus_write(iicbus, msg, sizeof(msg), &sent, CXM_I2C_TIMEOUT) != 0
270 || sent != sizeof(msg))
273 if (iicbus_write(iicbus, buf, len, &sent, CXM_I2C_TIMEOUT) != 0)
287 cxm_msp_init(struct cxm_softc *sc)
289 unsigned char rev1[2];
290 unsigned char rev2[2];
292 unsigned int nsettings;
293 const struct cxm_msp_setting *settings;
295 if (cxm_msp_dpl_reset (sc->iicbus, CXM_I2C_MSP3400) < 0)
298 if (cxm_msp_dpl_read(sc->iicbus, CXM_I2C_MSP3400, CXM_MSP3400C_DFP,
299 0x001e, rev1, sizeof(rev1)) != sizeof(rev1))
302 if (cxm_msp_dpl_read(sc->iicbus, CXM_I2C_MSP3400, CXM_MSP3400C_DFP,
303 0x001f, rev2, sizeof(rev2)) != sizeof(rev2))
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);
311 * MSP 34x5D, 34x5G, and MSP 44x8G are the
312 * only audio decoders currently supported.
315 if (strncmp(&sc->msp_name[0], "34", 2) == 0
316 && strncmp(&sc->msp_name[3], "5D", 2) == 0)
318 else if (strncmp(&sc->msp_name[0], "34", 2) == 0
319 && strncmp(&sc->msp_name[3], "5G", 2) == 0)
321 else if (strncmp(&sc->msp_name[0], "44", 2) == 0
322 && strncmp(&sc->msp_name[3], "8G", 2) == 0)
325 device_printf(sc->dev, "unknown audio decoder MSP%s\n",
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;
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,
341 sizeof(settings[i].value))
342 != sizeof(settings[i].value))
345 if (cxm_msp_select_source(sc, cxm_tuner_source) < 0)
348 device_printf(sc->dev, "MSP%s audio decoder\n", sc->msp_name);
355 cxm_msp_mute(struct cxm_softc *sc)
358 unsigned int nsettings;
359 const struct cxm_msp_setting *settings;
361 nsettings = msp34xxx_mute.nsettings;
362 settings = msp34xxx_mute.settings;
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,
368 sizeof(settings[i].value))
369 != sizeof(settings[i].value))
377 cxm_msp_unmute(struct cxm_softc *sc)
380 unsigned int nsettings;
381 const struct cxm_msp_setting *settings;
383 nsettings = msp34xxx_unmute.nsettings;
384 settings = msp34xxx_unmute.settings;
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,
390 sizeof(settings[i].value))
391 != sizeof(settings[i].value))
399 cxm_msp_is_muted(struct cxm_softc *sc)
401 unsigned char volume[2];
403 if (cxm_msp_dpl_read(sc->iicbus, CXM_I2C_MSP3400, CXM_MSP3400C_DFP,
404 0x0000, volume, sizeof(volume)) != sizeof(volume))
407 return volume[0] == 0x00 || volume[0] == 0xff ? 1 : 0;
412 cxm_msp_select_source(struct cxm_softc *sc, enum cxm_source source)
415 unsigned int nsettings;
416 const struct cxm_msp_setting *settings;
420 nsettings = msp34xxx_select_fm.nsettings;
421 settings = msp34xxx_select_fm.settings;
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;
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;
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,
447 sizeof(settings[i].value))
448 != sizeof(settings[i].value))
456 cxm_msp_selected_source(struct cxm_softc *sc)
458 unsigned char dsp[2];
459 unsigned char source[2];
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;
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;
476 return cxm_unknown_source;
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;
484 return cxm_unknown_source;
486 switch (dsp[0] & 0x03) {
488 return cxm_line_in_source_composite;
491 return cxm_fm_source;
494 return cxm_unknown_source;
500 cxm_msp_autodetect_standard(struct cxm_softc *sc)
504 unsigned int nsettings;
505 const struct cxm_msp_setting *settings;
507 switch (cxm_msp_selected_source(sc)) {
508 case cxm_tuner_source:
512 case cxm_line_in_source_composite:
513 case cxm_line_in_source_svideo:
521 * Section 3.3.2.2 of the data sheet states:
523 * A general refresh of the STANDARD SELECT
524 * register is not allowed.
527 if (cxm_msp_dpl_reset (sc->iicbus, CXM_I2C_MSP3400) < 0)
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;
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,
541 sizeof(settings[i].value))
542 != sizeof(settings[i].value))
545 locked = cxm_msp_wait_for_lock(sc);
547 if (cxm_msp_select_source(sc, cxm_tuner_source) < 0)
555 cxm_msp_is_locked(struct cxm_softc *sc)
557 unsigned char source[2];
558 unsigned char standard[2];
560 if (cxm_msp_dpl_read(sc->iicbus, CXM_I2C_MSP3400, CXM_MSP3400C_DFP,
561 0x0008, source, sizeof(source)) != sizeof(source))
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 */
575 if (cxm_msp_dpl_read(sc->iicbus, CXM_I2C_MSP3400, CXM_MSP3400C_DEM,
576 0x007e, standard, sizeof(standard))
580 if (standard[0] >= 8 || (standard[0] == 0 && standard[1] == 0))
588 cxm_msp_wait_for_lock(struct cxm_softc *sc)
593 * Section 3.3.2.1 of the data sheet states:
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.
601 for (i = 0; i < 10; i++) {
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.
610 tsleep(&sc->iicbus, 0, "audio", hz / 20);
612 switch (cxm_msp_is_locked(sc)) {
624 device_printf(sc->dev, "audio decoder failed to lock\n");