xref: /dragonfly/sys/dev/video/cxm/cxm_msp34xxx.c (revision 1b722dce)
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] = (unsigned char)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