1/******************************************************************************
2/
3/	File:			MSP3430.cpp
4/
5/	Description:	Micronas Multistandard Sound Processor (MSP) interface.
6/
7/	Copyright 2001, Carlos Hasan
8/
9*******************************************************************************/
10
11#include <Debug.h>
12#include "MSP3430.h"
13
14enum MSP3430_register {
15    MSP3430_CONTROL					= 0x00,				// R/W control register
16    MSP3430_WR_DEM					= 0x10,				// Write demodulator register
17    MSP3430_RD_DEM					= 0x11,				// Read demodulator register
18    MSP3430_WR_DSP					= 0x12,				// Read DSP register
19    MSP3430_RD_DSP					= 0x13				// Write DSP register
20};
21
22enum MSP3430_control_register {
23	MSP3430_CONTROL_NORMAL			= 0x0000,			// normal operation mode
24	MSP3430_CONTROL_RESET			= 0x8000			// reset mode
25};
26
27enum MSP3430_wr_dem_register {
28	MSP3430_DEM_STANDARD_SEL		= 0x0020,			// standard selection
29	MSP3430_DEM_MODUS				= 0x0030,			// demodulator options
30	MSP3430_DEM_I2S_CONFIG			= 0x0040			// I2S configuration
31};
32
33enum MSP3430_rd_dem_register {
34	MSP3430_DEM_STANDARD_RES		= 0x007e,			// standard detection result
35	MSP3430_DEM_STATUS				= 0x0200			// status register
36};
37
38enum MSP3430_wr_dsp_register {
39	// preprocessing
40	MSP3430_DSP_FM_PRESCALE			= 0x000e,			// FM/AM analog signal prescale
41		MSP3430_DSP_PRE_FM			= BITS(15:8),
42		MSP3430_DSP_FM_MATRIX		= BITS(7:0),
43	MSP3430_DSP_NICAM_PRESCALE		= 0x0010,			// NICAM digital signal prescale
44		MSP34300_DSP_PRE_NICAM		= BITS(15:8),
45	MSP3430_DSP_PRE_I2S2			= 0x0012,			// I2S digital signal prescale
46	MSP3430_DSP_PRE_I2S1			= 0x0016,
47	MSP3430_DSP_PRE_SCART			= 0x000d,			// SCART prescale
48
49	// source select and output channel matrix
50	MSP3430_DSP_SRC_MAT_MAIN		= 0x0008,			// loudspeaker source and matrix
51		MSP3430_DSP_SRC_MAIN		= BITS(15:8),
52		MSP3430_DSP_MAT_MAIN		= BITS(7:0),
53	MSP3430_DSP_SRC_MAT_AUX			= 0x0009,			// headphone source and matrix
54		MSP3430_DSP_SRC_AUX			= BITS(15:8),
55		MSP3430_DSP_MAT_AUX			= BITS(7:0),
56	MSP3430_DSP_SRC_MAT_SCART1		= 0x000a,			// SCART1 source and matrix
57		MSP3430_DSP_SRC_SCART1		= BITS(15:8),
58		MSP3430_DSP_MAT_SCART1		= BITS(7:0),
59	MSP3430_DSP_SRC_MAT_SCART2		= 0x0041,			// SCART2 source and matrix
60		MSP3430_DSP_SRC_SCART2		= BITS(15:8),
61		MSP3430_DSP_MAT_SCART2		= BITS(7:0),
62	MSP3430_DSP_SRC_MAT_I2S			= 0x000b,			// I2S source and matrix
63		MSP3430_DSP_SRC_I2S			= BITS(15:8),
64		MSP3430_DSP_MAT_I2S			= BITS(7:0),
65	MSP3430_DSP_SRC_MAT_QPEAK		= 0x000c,			// QuasiPeak detector source and matrix
66		MSP3430_DSP_SRC_QPEAK		= BITS(15:8),
67		MSP3430_DSP_MAT_QPEAK		= BITS(7:0),
68
69	// loudspeaker and headphone processing
70	MSP3430_DSP_VOL_MAIN			= 0x0000,			// loudspeaker volume
71	MSP3430_DSP_VOL_AUX				= 0x0006,			// headphone volume
72	MSP3430_DSP_AVC					= 0x0029,			// automatic volume correction
73	MSP3430_DSP_BAL_MAIN			= 0x0001,			// loudspeaker balance
74	MSP3430_DSP_BAL_AUX				= 0x0030,			// headphone balance
75	MSP3430_DSP_TONE_MODE			= 0x0020,			// select bass/treble or equalizer
76	MSP3430_DSP_BASS_MAIN			= 0x0002,			// loudspeaker bass
77	MSP3430_DSP_BASS_AUX			= 0x0031,			// headphone bass
78	MSP3430_DSP_TREB_MAIN			= 0x0003,			// loudspeaker treble
79	MSP3430_DSP_TREB_AUX			= 0x0032,			// headphone treble
80	MSP3430_DSP_EQUAL_BAND1			= 0x0021,			// equalizer coefficients
81	MSP3430_DSP_EQUAL_BAND2			= 0x0022,
82	MSP3430_DSP_EQUAL_BAND3			= 0x0023,
83	MSP3430_DSP_EQUAL_BAND4			= 0x0024,
84	MSP3430_DSP_EQUAL_BAND5			= 0x0025,
85	MSP3430_DSP_LOUD_MAIN			= 0x0004,			// loudspeaker loudness
86	MSP3430_DSP_LOUD_AUX			= 0x0033,			// headphone loudness
87	MSP3430_DSP_SPAT_MAIN			= 0x0005,			// spatial effects
88
89	// subwoofer output channel
90	MSP3430_DSP_SUBW_LEVEL			= 0x002c,			// subwoofer level
91	MSP3430_DSP_SUBW_FREQ			= 0x002d,			// subwoofer frequency
92
93	// micronas dynamic bass
94	MSP3430_DSP_MDB_STR				= 0x0068,			// MDB strength
95	MSP3430_DSP_MDB_LIM				= 0x0069,			// MDB limit
96	MSP3430_DSP_MDB_HMC				= 0x006a,			// MDB harmonic content
97	MSP3430_DSP_MDB_LP				= 0x006b,			// MDB low frequency
98	MSP3430_DSP_MDB_HP				= 0x006c,			// MDB high frequency
99
100	// SCART output channel
101	MSP3430_DSP_VOL_SCART1			= 0x0007,			// SCART1 volume
102	MSP3430_DSP_VOL_SCART2			= 0x0040,			// SCART2 volume
103
104	// SCART switches and digital I/O pins
105	MSP3430_DSP_ACB_REG				= 0x0013,			// SCART switches
106	MSP3430_DSP_BEEPER				= 0x0014			// Beeper volume and frequency
107};
108
109enum MSP3430_rd_dsp_register {
110	// quasi-peak detector readout
111	MSP3430_DSP_QPEAK_L				= 0x0019,			// Quasipeak detector left and right
112	MSP3430_DSP_QPEAK_R				= 0x001a,
113
114	// MSP 34x0G version readout
115	MSP3430_DSP_MSP_HARD_REVISION	= 0x001e,			// MSP hardware and revision
116		MSP3430_DSP_MSP_HARD		= BITS(15:8),
117		MSP3430_DSP_MSP_REVISION	= BITS(7:0),
118	MSP3430_DSP_MSP_PRODUCT_ROM		= 0x001f,			// MSP product and ROM version
119		MSP3430_DSP_MSP_PRODUCT		= BITS(15:8),
120		MSP3430_DSP_MSP_ROM			= BITS(7:0)
121};
122
123enum MSP3430_sound_standard {
124	C_MSP3430_AUTOMATIC			= 0x0001,	// Automatic Detection (*)
125	C_MSP3430_M_FM_STEREO		= 0x0002,	// NTSC M/N (*)
126	C_MSP3430_BG_FM_STEREO		= 0x0003,	// PAL B/G (*)
127	C_MSP3430_DK1_FM_STEREO		= 0x0004,	// (*)
128	C_MSP3430_DK2_FM_STEREO		= 0x0005,	//
129	C_MSP3430_DK_FM_MONO		= 0x0006,	//
130	C_MSP3430_DK3_FM_STEREO		= 0x0007,	//
131	C_MSP3430_BG_NICAM_FM		= 0x0008,	// PAL B/G (*)
132	C_MSP3430_L_NICAM_AM		= 0x0009,	// (*)
133	C_MSP3430_I_NICAM_FM		= 0x000A,	// PAL I (*)
134	C_MSP3430_DK_NICAM_FM		= 0x000B,	// (*)
135	C_MSP3430_DK_NICAM_FM_HDEV2	= 0x000C,
136	C_MSP3430_DK_NICAM_FM_HDEV3	= 0x000D,
137	C_MSP3430_BTSC_STEREO		= 0x0020,	// BTSC Stereo (*)
138	C_MSP3430_BTSC_MONO_SAP		= 0x0021,
139	C_MSP3430_M_JAPAN_STEREO	= 0x0030,	// NTSC Japan (*)
140	C_MSP3430_FM_RADIO			= 0x0040,	// FM Radio (*)
141	C_MSP3430_SAT_MONO			= 0x0050,	// Satellite Mono
142	C_MSP3430_SAT_STEREO		= 0x0051,	// Satellite Stereo
143	C_MSP3430_SAT_ASTRA_RADIO	= 0x0060	// Astra Digital Radio
144};
145
146enum MSP3430_channel_source {
147	C_MSP3430_SOURCE_FM			= 0x00,		// FM/AM Mono
148	C_MSP3430_SOURCE_STEREO		= 0x01,		// Stereo or A/B (NICAM)
149	C_MSP3430_SOURCE_SCART		= 0x02,		// SCART Input
150	C_MSP3430_SOURCE_LANG_A		= 0x03,		// Stereo/Language A
151	C_MSP3430_SOURCE_LANG_B		= 0x04,		// Stereo/Language B
152	C_MSP3430_SOURCE_I2S1		= 0x05,		// I2S1 Input
153	C_MSP3430_SOURCE_I2S2		= 0x06		// I2S2 Input
154};
155
156enum MSP3430_channel_matrix {
157	C_MSP3430_MATRIX_SOUND_A	= 0x00,		// Sound A Mono
158	C_MSP3430_MATRIX_SOUND_B	= 0x10,		// Sound B Mono
159	C_MSP3430_MATRIX_STEREO		= 0x20,		// Stereo
160	C_MSP3430_MATRIX_MONO		= 0x30		// Mono
161};
162
163
164
165/*
166
167-------------------------------------------------------------------
168System			Sound			Sound				Color
169				Carrier (MHz)	Modulation			System
170-------------------------------------------------------------------
171Satellite		6.5/5.85		FM-Mono/NICAM		PAL
172				6.5				FM-Mono
173				7.02/7.2		FM-Stereo			PAL
174				7.38/7.56		ASTRA Digital Radio
175				etc.
176				4.5/4.724212	FM-Stereo (A2)		NTSC
177-------------------------------------------------------------------
178NTSC M/N		4.5				FM-FM (EIA-J)		NTSC
179-------------------------------------------------------------------
180				4.5				BTSC Stereo + SAP	NTSC, PAL
181FM-Radio		10.7			FM-Stereo Radio		.
182-------------------------------------------------------------------
183
184
185
186
187
188*/
189
190CMSP3430::CMSP3430(CI2CPort & port)
191	:	fPort(port),
192		fAddress(0)
193{
194	PRINT(("CMSP3430::CMSP3430()\n"));
195
196	if( fPort.InitCheck() == B_OK ) {
197		for (fAddress = 0x80; fAddress <= 0x80; fAddress += 0x02) {
198	        if (fPort.Probe(fAddress)) {
199	        	PRINT(("CMSP3430::CMSP3430() - Sound Processor found at I2C port 0x%02x\n", fAddress));
200				break;
201			}
202		}
203	}
204	if( InitCheck() != B_OK )
205		PRINT(("CMSP3430::CMSP3430() - Sound processor not found!\n"));
206}
207
208CMSP3430::~CMSP3430()
209{
210	PRINT(("CMSP3430::~CMSP3430()\n"));
211}
212
213status_t CMSP3430::InitCheck() const
214{
215	status_t res;
216
217	res = fPort.InitCheck();
218	if( res != B_OK )
219		return res;
220
221	return (fAddress >= 0x80 && fAddress <= 0x80) ? B_OK : B_ERROR;
222}
223
224void CMSP3430::SetEnable(bool enable)
225{
226	PRINT(("CMSP3430::SetEnable(%d)\n", enable));
227
228	SetControlRegister(MSP3430_CONTROL_RESET);
229	SetControlRegister(MSP3430_CONTROL_NORMAL);
230
231	if (enable) {
232		SetRegister(MSP3430_WR_DEM, MSP3430_DEM_MODUS, 0x2003);
233		SetRegister(MSP3430_WR_DEM, MSP3430_DEM_STANDARD_SEL, C_MSP3430_BTSC_STEREO); // 0x20
234		SetRegister(MSP3430_WR_DSP, MSP3430_DSP_FM_PRESCALE, 0x2403);
235		SetRegister(MSP3430_WR_DSP, MSP3430_DSP_SRC_MAT_MAIN, 0x0320);
236		SetRegister(MSP3430_WR_DSP, MSP3430_DSP_VOL_MAIN, 0x7300);
237		SetRegister(MSP3430_WR_DSP, MSP3430_DSP_SRC_MAT_MAIN, 0x0320);
238	}
239	else {
240		SetRegister(MSP3430_WR_DSP, MSP3430_DSP_VOL_MAIN, 0x0000);
241	}
242}
243
244#if 0
245#pragma mark -
246
247void CMSP3430::SetI2S(bool fast)
248{
249	// select 2 x 16 bit (1024 MHz) or 2 x 32 bit (2048) frequency
250	SetRegister(MSP3430_WR_DEM, I2S_CONFIG, fast ? 0x0001 : 0x0000);
251}
252
253bool CMSP3430::IsSAP()
254{
255	// bilingual sound mode or SAP present?
256	if ((Register(MSP3430_RD_DEM, STATUS) & 0x0100) != 0x0000)
257		return true;
258	return false;
259}
260
261bool CMSP3430::IsMonoNICAM()
262{
263	// independent mono sound (only NICAM)?
264	if ((Register(MSP3430_RD_DEM, STATUS) & 0x0080) != 0x0000)
265		return true;
266	return false;
267}
268
269bool CMSP3430::IsStereo()
270{
271	// mono/stereo indication
272	if ((Register(MSP3430_RD_DEM, STATUS) & 0x0040) != 0x0000)
273		return true;
274	return false;
275}
276
277bool CMSP3430::IsFM()
278{
279	// is analog sound standard (FM or AM) active?
280	if ((Register(MSP3430_RD_DEM, STATUS) & 0x0120) == 0x0000)
281		return true;
282	return false;
283}
284
285bool CMSP3430::IsNICAM()
286{
287	// digital sound (NICAM) available?
288	if ((Register(MSP3430_RD_DEM, STATUS) & 0x0100) == 0x0100)
289		return true;
290	return false;
291}
292
293bool CMSP3430::HasSecondaryCarrier()
294{
295	// detected secondary carrier (2nd A2 or SAP sub-carrier)
296	if ((Register(MSP3430_RD_DEM, STATUS) & 0x0004) != 0x0000)
297		return true;
298	return false;
299}
300
301bool CMSP3430::HasPrimaryCarrier()
302{
303	// detected primary carrier (Mono or MPX carrier)
304	if ((Register(MSP3430_RD_DEM, STATUS) & 0x0002) != 0x0000)
305		return true;
306	return false;
307}
308
309void CMSP3430::SetStandard(MSP3430_standard standard)
310{
311	fStandard = standard;
312
313	// set sound mode, FM/AM matrix, source channels
314	switch (standard) {
315	C_MSP3430_STANDARD_BG_FM:
316	C_MSP3430_STANDARD_DK_FM:
317		// Sound Mode	FM Matrix		FM/AM Source	Stereo A/B Source
318		// ---------------------------------------------------------------
319		// Mono 		Sound A Mono	Mono			Mono
320	C_MSP3430_STANDARD_M_KOREA:
321	C_MSP3430_STANDARD_M_JAPAN:
322		// Stereo 		Korean Stereo	Stereo			Stereo
323		// Stereo 		German Stereo	Stereo			Stereo
324		// Lang A & B	No Matrix		Left=A, Right=B	Left=A, Right=B
325
326	C_MSP3430_STANDARD_BG_NICAM:
327
328
329	}
330
331	SetDemodulator();
332}
333
334void CMSP3430::SetFMPrescale(int gain, MSP3430_fm_matrix matrix, bool mute)
335{
336	// set FM/AM prescale and FM matrix mode
337	fFMPrescale = (mute ? 0x00 : Clamp(gain, 0x00, 0x7f)) << 8;
338	/// see table 6--17, Appendix B.
339	switch (matrix) {
340	case C_MSP3430_MATRIX_NONE:
341		fFMPrescale |= 0x0000;
342		break;
343	case C_MSP3430_MATRIX_GERMAN_STEREO_BG:
344		fFMPrescale |= 0x0001;
345		break;
346	case C_MSP3430_MATRIX_KOREAN_STEREO:
347		fFMPrescale |= 0x0002;
348		break;
349	case C_MSP3430_MATRIX_SOUND_A_MONO:
350		fFMPrescale |= 0x0003;
351		break;
352	case C_MSP3430_MATROX_SOUND_B_MONO:
353		fFMPrescale |= 0x0004;
354		break;
355	}
356
357	SetDemodulator();
358}
359
360void CMSP3430::SetNICAMPrescale(int gain, bool mute)
361{
362	// set NICAM prescale to 0..12 dB
363	fNICAMPrescale = (mute ? 0x00 : Clamp(0x20 + (0x5f * gain) / 12, 0x20, 0x7f)) << 8;
364
365	SetDemodulator();
366}
367
368void CMSP3430::SetI2SPrescale(int gain, bool mute)
369{
370	// set digital I2S input prescale 0..18 dB
371	fI2SPrescale = (mute ? 0x00 : Clamp(0x10 + (0x7f * gain) / 18, 0x10, 0x7f) << 8;
372	SetSCARTInput();
373}
374
375void CMSP3430::SetSCARTPrescale(int gain, bool mute)
376{
377	// set SCART input signal prescale 0..14 dB
378	fSCARTPrescale = (mute ? 0x00 : Clamp(0x19 + (0x66 * gain) / 14, 0x19, 0x7f) << 8;
379	SetSCARTInput();
380}
381
382void CMSP3430::SetSource(MSP3430_source_channel speaker,
383						 MSP3430_source_channel headphone,
384						 MSP3430_source_channel scart1,
385						 MSP3430_source_channel scart2,
386						 MSP3430_source_channel i2s,
387						 MSP3430_source_channel qpeak)
388{
389	// set source and matrix..
390	// select FM/AM, Stereo A/B, Stereo A, Stereo B, SCART, I2S1 or I2S2 source channels
391	// and select Sound A Mono, Sound B Mono, Stereo or Mono mode
392	fSrcMain = loudspeaker;
393	fSrcAux = headphone;
394	fSrcSCART1 = scart1;
395	fSrcSCART2 = scart2;
396	fSrcI2S = i2s;
397	fSrcQPeak = qpeak;
398	SetOutputChannels();
399}
400
401
402void CMSP3430::SetVolume(int level, bool mute, MSP3430_clipping_mode mode,
403				MSP3430_automatic_volume automatic)
404{
405	// set volume between -114 dB and 12 dB with 1 dB step size
406	fVolume = (mute ? 0x00 : Clamp(level + 0x73, 0x01, 0x7f)) << 8;
407	switch (mode) {
408	case C_MSP3430_CLIP_REDUCE_VOLUME:
409		fVolume |= 0x00;
410		break;
411	case C_MSP3430_CLIP_REDUCE_TONE:
412		fVolume |= 0x01;
413		break;
414	case C_MSP3430_CLIP_COMPROMISE:
415		fVolume |= 0x02;
416		break;
417	case C_MSP3430_CLIP_DYNAMIC:
418		fVolume |= 0x03;
419		break;
420	}
421
422	// enable/disable automatic volume correction
423	switch (automatic) {
424	case C_MSP3430_AUTOMATIC_VOLUME_OFF:
425		fAVC = 0x0000;
426		break;
427	case C_MSP3430_AUTOMATIC_VOLUME_20_MS:
428		fAVC = 0x8100;
429		break;
430	case C_MSP3430_AUTOMATIC_VOLUME_2_SEC:
431		fAVC = 0x8200;
432		break;
433	case C_MSP3430_AUTOMATIC_VOLUME_4_SEC:
434		fAVC = 0x8400;
435		break;
436	case C_MSP3430_AUTOMATIC_VOLUME_8_SEC:
437		fAVC = 0x8800;
438		break;
439	}
440	SetOutputChannels();
441}
442
443void CMSP3430::SetBalance(int balance, MSP3430_balance_mode mode)
444{
445	switch (mode) {
446	case C_MSP3430_BALANCE_LINEAR:
447		// set balance between -100% (right) and 100% (left)
448		fBalance = (((0x7f * Clamp(balance, -100, 100)) / 100) << 8) + 0x0000;
449		break;
450
451	case C_MSP3430_BALANCE_LOGARITHMIC:
452		// set balance between -128 dB (right) and 127 dB (left)
453		fBalance = (((0x7f * Clamp(balance, -128, 127)) / 127) << 8) + 0x0001;
454		break;
455	}
456	SetOutputChannels();
457}
458
459
460void CMSP3430::SetEqualizer(int bass, int treble)
461{
462	// set bass to -12..12 dB or 14..20 dB
463	if (bass <= 12)
464		fBass = Clamp(0x00 + 8 * bass, -0x60, 0x60) << 8;
465	else
466		fBass = Clamp(0x68 + 4 * (bass - 14), 0x68, 0x7f) << 8;
467
468	// set treble to -12..15 dB
469	fTreble = Clamp(0x00 + 8 * treble, -0x60, 0x78) << 8;
470
471	// enable bass/treble and disable equalizer
472	fToneControl = 0x00;
473	fEqualizer[0] = fEqualizer[1] = fEqualizer[2] =
474	fEqualizer[3] = fEqualizer[4] = fEqualizer[5] = 0x00;
475
476	SetOutputChannels();
477}
478
479void CMSP3430::SetEqualizer(int gain[5])
480{
481	// set five band equalizer (120 Hz, 500 Hz, 1500 Hz, 5000 Hz, 10000 Hz)
482	for (int index = 0; index < 5; index++)
483		fEqualizer[index] = Clamp(0x00 + 8 * gain[index], -0x60, 0x60) << 8;
484
485	// disable bass/treble and enable equalizer
486	fToneControl = 0xff;
487	fBass = fTreble = 0x00;
488
489	SetOutputChannels();
490}
491
492void CMSP3430::SetLoudness(int loudness, MSP3430_loudness_mode mode)
493{
494	// set loudness gain between 0 dB and 17 dB
495	fLoudness = Clamp(0x00 + 4 * loudness, 0x00, 0x44) << 8;
496	switch (mode) {
497	case C_MSP3430_LOUDNESS_NORMAL:
498		fLoudness |= 0x0000;
499		break;
500	case C_MSP3430_LOUDNESS_SUPER_BASS:
501		fLoudness |= 0x0004;
502		break;
503	}
504
505	SetOutputChannels();
506}
507
508void CMSP3430::SetSpatial(int strength, MSP3430_spatial_mode mode, MSP3430_spatial_highpass pass)
509{
510	// enlarge or reduce spatial effects -100% to +100%
511	fSpatial = Clamp(0x00 + (0x7f * strength) / 100, -0x80, 0x7f) << 8;
512	switch (mode) {
513	case C_MSP3430_SPATIAL_MODE_A:
514		fSpatial |= 0x0000;
515		break;
516	case C_MSP3430_SPATIAL_MODE_B:
517		fSpatial |= 0x0020;
518		break;
519	}
520	switch (pass) {
521	case C_MSP3430_SPATIAL_MAX_HIGH_PASS:
522		fSpatial |= 0x0000;
523		break;
524	case C_MSP3430_SPATIAL_2_3_HIGH_PASS:
525		fSpatial |= 0x0002;
526		break;
527	case C_MSP3430_SPATIAL_1_3_HIGH_PASS:
528		fSpatial |= 0x0004;
529		break;
530	case C_MSP3430_SPATIAL_MIN_HIGH_PASS:
531		fSpatial |= 0x0006;
532		break;
533	case C_MSP3430_SPATIAL_AUTO_HIGH_PASS:
534		fSpatial |= 0x0008;
535		break;
536	}
537	SetOutputChannels();
538}
539
540void CMSP3430::SetSubwoofer(int level, int frequency, bool mute, MSP3430_subwoofer_mode mode)
541{
542	// set subwoofer level between -128 dB and 12 dB
543	fSubwooferLevel = (mute ? 0x80 : Clamp(0x00 + level, -128, 12)) << 8;
544
545	// set subwoofer corner frequency between 50..400 Hz
546	fSubwooferFrequency = Clamp(frequency / 10, 5, 40) << 8;
547	switch (mode) {
548	case C_MSP3430_SUBWOOFER_UNFILTERED:
549		fSubwooferFrequency |= 0x0000;
550		break;
551	case C_MSP3430_SUBWOOFER_HIGH_PASS:
552		fSubwooferFrequency |= 0x0001;
553		break;
554	case C_MSP3430_SUBWOOFER_MDB_MAIN:
555		fSubwooferFrequency |= 0x0002;
556		break;
557	}
558
559	// disable MDB?
560	fMDBStrength = 0x0000;
561	fMDBLimit = 0x0000;
562	fMDBHarmonic = 0x0000;
563	fMDBHighPass = 0x0a00;
564	fMDBLowPass = 0x0a00;
565
566	SetSubwooferAndMDBOutputChannels();
567}
568
569void CMSP3430::SetMDB(int strength, int limit, int harmonic,
570					  int minfrequency, int maxfrequency, bool mute)
571{
572	// set MDB effect strength 0..127
573	fMDBStrength = (mute ? 0x00 : Clamp(strength, 0x00, 0x7f)) << 7;
574
575	// set MDB amplitude limit between -32..0 dBFS
576	fMDBLimit = Clamp(limit, -32, 0) << 8;
577
578	// set MDB harmonic content 0..100%
579	fMDBHarmonic = Clamp((0x7f * harmonic) / 100, 0x00, 0x7f) << 8;
580
581	// set MDB low pass corner frequency 50..300 Hz
582	fMDBLowPass = Clamp(minFrequency / 10, 5, 30) << 8;
583
584	// set MDB high pass corner frequency 20..300 Hz
585	fMDBHighPass = Clamp(maxFrequency / 10, 2, 30) << 8;
586
587	// disable subwoofer level adjustments
588	fSubwooferLevel = 0x0000;
589	fSubwooferFrequency = 0x0002;
590
591	// for MDB, use dynamic clipping mode
592	fVolume = (fVolume & ~0x000f) | 0x0003;
593
594	SetSubwooferAndMDBOutputChannels();
595}
596
597void CMSP3430::SetSCART(int level1, int level2, bool mute,
598						MSP3430_scart_input input,
599						MSP3430_scart_output output1,
600						MSP3430_scart_output output2)
601{
602	// set volume SCART1/2 output channel -114..12 dB
603	fSCART1Volume = (mute ? 0x00 : Clamp(0x73 + (0x7f * level1) / 12, 0x00, 0x7f)) << 8;
604	fSCART2Volume = (mute ? 0x00 : Clamp(0x73 + (0x7f * level2) / 12, 0x00, 0x7f)) << 8;
605
606	fSCART1Volume |= 0x0001;
607	fSCART2Volume |= 0x0001;
608
609	// SCART DSP input select
610	fACB = 0x0000;
611	switch (input) {
612	case C_MSP3430_SCART_INPUT_SCART1:
613		fACB = 0x0000;
614		break;
615	case C_MSP3430_SCART_INPUT_MONO:
616		fACB = 0x0100;
617		break;
618	case C_MSP3430_SCART_INPUT_SCART2:
619		fACB = 0x0200;
620		break;
621	case C_MSP3430_SCART_INPUT_SCART3:
622		fACB = 0x0300;
623		break;
624	case C_MSP3430_SCART_INPUT_SCART4:
625		fACB = 0x0020;
626		break;
627	case C_MSP3430_SCART_INPUT_MUTE:
628		fACB = 0x0320;
629		break;
630	}
631
632	// SCART1 output select
633	switch (output1) {
634	case C_MSP3430_SCART_OUTPUT_SCART3:
635		fACB |= 0x0000;
636		break;
637	case C_MSP3430_SCART_OUTPUT_SCART2:
638		fACB |= 0x0400;
639		break;
640	case C_MSP3430_SCART_OUTPUT_MONO:
641		fACB |= 0x0800;
642		break;
643	case C_MSP3430_SCART_OUTPUT_SCART1_DA:
644		fACB |= 0x0c00;
645		break;
646	case C_MSP3430_SCART_OUTPUT_SCART2_DA:
647		fACB |= 0x0080;
648		break;
649	case C_MSP3430_SCART_OUTPUT_SCART1_INPUT:
650		fACB |= 0x0480;
651		break;
652	case C_MSP3430_SCART_OUTPUT_SCART4_INPUT:
653		fACB |= 0x0880;
654		break;
655	case C_MSP3430_SCART_OUTPUT_MUTE:
656		fACB |= 0x0c80;
657		break;
658	}
659
660	// SCART2 output select
661	switch (output2) {
662	case C_MSP3430_SCART_OUTPUT_SCART3:
663		fACB |= 0x0000;
664		break;
665	case C_MSP3430_SCART_OUTPUT_SCART2:
666		fACB |= 0x1000;
667		break;
668	case C_MSP3430_SCART_OUTPUT_MONO:
669		fACB |= 0x2000;
670		break;
671	case C_MSP3430_SCART_OUTPUT_SCART1_DA:
672		fACB |= 0x3000;
673		break;
674	case C_MSP3430_SCART_OUTPUT_SCART2_DA:
675		fACB |= 0x0200;
676		break;
677	case C_MSP3430_SCART_OUTPUT_SCART1_INPUT:
678		fACB |= 0x1200;
679		break;
680	case C_MSP3430_SCART_OUTPUT_SCART4_INPUT:
681		fACB |= 0x2200;
682		break;
683	case C_MSP3430_SCART_OUTPUT_MUTE:
684		fACB |= 0x3200;
685		break;
686	}
687
688	SetSCARTSignalPath();
689	SetOutputChannels();
690}
691
692void CMSP3430::SetBeeper(int volume, MSP3430_beeper_frequency frequency, bool mute)
693{
694	// set beeper volume 0..127
695	int beeper = (mute ? 0x00 : Clamp(volume, 0x00, 0x7f)) << 8;
696
697	// set beeper frequency 16, 1000 or 4000 Hz
698	switch (frequency) {
699	case C_MSP3430_BEEPER_16_HZ:
700		beeper |= 0x0001;
701		break;
702	case C_MSP3430_BEEPER_1_KHZ:
703		beeper |= 0x0040;
704		break;
705	case C_MSP3430_BEEPER_4_KHZ:
706		beeper |= 0x00ff;
707		break;
708	}
709	SetRegister(MSP3430_WR_DSP, BEEPER, beeper);
710}
711
712void CMSP3430::SetQuasiPeak(int left, int right)
713{
714	// set quasipeak detector readout -32768..32767
715	fQPeakLeft = left;
716	fQPeakRight = right;
717
718	SetOutputChannels();
719}
720
721void CMSP3430::GetVersion(char * version)
722{
723	int revision = Register(MSP3430_RD_DSP, MSP_HARD_REVISION);
724	int product = Register(MSP3430_RD_DSP, MSP_PRODUCT_ROM);
725
726	sprintf(version, "MSP34%02d%c-%c%d",
727		((product  & MSP_PRODUCT)  >> 8),
728		((revision & MSP_REVISION) >> 0) + 0x40,
729		((revision & MSP_HARD)     >> 8) + 0x40,
730		((product  & MSP_ROM)      >> 0));
731}
732
733#pragma mark -
734
735void CMSP3430::Reset()
736{
737	// software reset
738	SetControlRegister(MSP3430_CONTROL_RESET);
739	SetControlRegister(MSP3430_CONTROL_NORMAL);
740}
741
742void CMSP3430::SetSCARTSignalPath()
743{
744	// select SCART DSP input and output
745	SetRegister(MSP3430_WR_DSP, MSP3430_DSP_ACB_REG,fACB);
746}
747
748void CMSP3430::SetDemodulator()
749{
750	// set preferred mode and sound IF input
751	// (automatic, enable STATUS change, detect 4.5 MHz carrier as BTSC)
752	SetRegister(MSP3430_WR_DEM, MODUS, 0x2003);
753
754	// set preferred prescale (FM and NICAM)
755	SetRegister(MSP3430_WR_DSP, PRE_FM, fFMPrescale);
756	SetRegister(MSP3430_WR_DSP, PRE_NICAM, fNICAMPrescale);
757
758	// automatic sound standard selection
759	SetRegister(MSP3430_WR_DEM, MSP3430_DEM_STANDARD_SEL, 0x0001);
760
761	// readback the detected TV sound or FM-radio standard
762	while ((fStandard = Register(MSP3430_RD_DEM, STANDARD_RES)) >= 0x0800)
763		snooze(100000);
764}
765
766void CMSP3430::SetSCARTInput()
767{
768	// select preferred prescale for SCART and I2C
769	SetRegister(MSP3430_WR_DSP, PRE_SCART, fSCARTPrescale);
770	SetRegister(MSP3430_WR_DSP, PRE_I2S1, fI2SPrescale);
771	SetRegister(MSP3430_WR_DSP, PRE_I2S2, fI2SPrescale);
772}
773
774void CMSP3430::SetOutputChannels()
775{
776	// select source channel and matrix for each output
777	SetRegister(MSP3430_WR_DSP, MSP3430_DSP_SRC_MAT_MAIN, fMainMatrix);
778	SetRegister(MSP3430_WR_DSP, MSP3430_DSP_SRC_MAT_AUX, fAuxMatrix);
779	SetRegister(MSP3430_WR_DSP, MSP3430_DSP_SRC_MAT_SCART1, fSCART1Matrix);
780	SetRegister(MSP3430_WR_DSP, MSP3430_DSP_SRC_MAT_SCART2, fSCART2Matrix);
781	SetRegister(MSP3430_WR_DSP, MSP3430_DSP_SRC_MAT_I2S, fI2SMatrix);
782	SetRegister(MSP3430_WR_DSP, MSP3430_DSP_SRC_MAT_QPEAK, fQPeakMatrix);
783
784	// set audio baseband processing
785	SetRegister(MSP3430_WR_DSP, MSP3430_DSP_TONE_MODE, fToneControl);
786	SetRegister(MSP3430_WR_DSP, MSP3430_DSP_BASS_MAIN, fBass);
787	SetRegister(MSP3430_WR_DSP, MSP3430_DSP_BASS_AUX, fBass);
788	SetRegister(MSP3430_WR_DSP, MSP3430_DSP_TREB_MAIN, fTreble);
789	SetRegister(MSP3430_WR_DSP, MSP3430_DSP_TREB_AUX, fTreble);
790	SetRegister(MSP3430_WR_DSP, MSP3430_DSP_EQUAL_BAND1, fEqualizer[0]);
791	SetRegister(MSP3430_WR_DSP, MSP3430_DSP_EQUAL_BAND2, fEqualizer[1]);
792	SetRegister(MSP3430_WR_DSP, MSP3430_DSP_EQUAL_BAND3, fEqualizer[2]);
793	SetRegister(MSP3430_WR_DSP, MSP3430_DSP_EQUAL_BAND4, fEqualizer[3]);
794	SetRegister(MSP3430_WR_DSP, MSP3430_DSP_EQUAL_BAND5, fEqualizer[4]);
795	SetRegister(MSP3430_WR_DSP, MSP3430_DSP_LOUD_MAIN, fLoudness);
796	SetRegister(MSP3430_WR_DSP, MSP3430_DSP_LOUD_AUX, fLoudness);
797	SetRegister(MSP3430_WR_DSP, MSP3430_DSP_SPAT_MAIN, fSpatial);
798
799	// select volume for each output channel
800	SetRegister(MSP3430_WR_DSP, MSP3430_DSP_VOL_MAIN, fVolume);
801	SetRegister(MSP3430_WR_DSP, MSP3430_DSP_VOL_AUX, fVolume);
802	SetRegister(MSP3430_WR_DSP, MSP3430_DSP_AVC, fAVC);
803	SetRegister(MSP3430_WR_DSP, MSP3430_DSP_BAL_MAIN, fBalance);
804	SetRegister(MSP3430_WR_DSP, MSP3430_DSP_BAL_AUX, fBalance);
805
806	SetRegister(MSP3430_WR_DSP, VOL_SCART1, fSCART1Volume);
807	SetRegister(MSP3430_WR_DSP, VOL_SCART2, fSCART2Volume);
808
809	// set quasipeak detector readout
810	SetRegister(MSP3430_WR_DSP, QPEAK_L, fQPeakLeft);
811	SetRegister(MSP3430_WR_DSP, QPEAK_R, fQPeakRight);
812}
813
814void CMSP3430::SetSubwooferAndMDBOutputChannels()
815{
816	// set subwoofer output channel
817	SetRegister(MSP3430_WR_DSP, SUBW_LEVEL, fSubwooferLevel);
818	SetRegister(MSP3430_WR_DSP, SUBW_FREQ, fSubwooferFrequency);
819
820	// set MDB control
821	SetRegister(MSP3430_WR_DSP, MDB_STR, fMDBStrength);
822	SetRegister(MSP3430_WR_DSP, MDB_LIM, fMDBLimit);
823	SetRegister(MSP3430_WR_DSP, MDB_HMC, fMDBHarmonic);
824	SetRegister(MSP3430_WR_DSP, MDB_LP, fMDBLowPass);
825	SetRegister(MSP3430_WR_DSP, MDB_HP, fMDBHighPass);
826}
827
828#pragma mark -
829#endif
830
831int CMSP3430::ControlRegister()
832{
833	char message[1], result[2];
834
835	message[0] = MSP3430_CONTROL;
836	if (fPort.Write(fAddress, message, sizeof(message), result, sizeof(result)))
837		return ((result[0] << 8) & 0xff00) + ((result[1] << 0) & 0x00ff);
838	return 0;
839}
840
841void CMSP3430::SetControlRegister(int value)
842{
843	char message[3];
844
845	message[0] = MSP3430_CONTROL;
846	message[1] = value >> 8;
847	message[2] = value >> 0;
848
849	if (!fPort.Write(fAddress, message, sizeof(message)))
850		PRINT(("CMSP3430::SetControl() - error\n"));
851}
852
853int CMSP3430::Register(int address, int subaddress)
854{
855	char message[3], response[2];
856
857	message[0] = address;
858	message[1] = subaddress >> 8;
859	message[2] = subaddress >> 0;
860
861	if (fPort.Write(fAddress, message, sizeof(message), response, sizeof(response)))
862		return ((response[0] << 8) & 0xff00) + ((response[1] << 0) & 0x00ff);
863	return 0;
864}
865
866void CMSP3430::SetRegister(int address, int subaddress, int value)
867{
868	char message[5];
869
870	message[0] = address;
871	message[1] = subaddress >> 8;
872	message[2] = subaddress >> 0;
873	message[3] = value >> 8;
874	message[4] = value >> 0;
875
876	if (!fPort.Write(fAddress, message, sizeof(message)))
877		PRINT(("CMSP3430::SetRegister() - error\n"));
878}
879