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
cxm_msp_dpl_reset(device_t iicbus,int i2c_addr)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
cxm_msp_dpl_read(device_t iicbus,int i2c_addr,unsigned char dev,unsigned int addr,char * buf,int len)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
cxm_msp_dpl_write(device_t iicbus,int i2c_addr,unsigned char dev,unsigned int addr,const char * buf,int len)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
cxm_msp_init(struct cxm_softc * sc)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
cxm_msp_mute(struct cxm_softc * sc)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
cxm_msp_unmute(struct cxm_softc * sc)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
cxm_msp_is_muted(struct cxm_softc * sc)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
cxm_msp_select_source(struct cxm_softc * sc,enum cxm_source source)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
cxm_msp_selected_source(struct cxm_softc * sc)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
cxm_msp_autodetect_standard(struct cxm_softc * sc)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
cxm_msp_is_locked(struct cxm_softc * sc)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
cxm_msp_wait_for_lock(struct cxm_softc * sc)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