xref: /haiku/src/add-ons/kernel/drivers/audio/echo/generic/CLayla24DspCommObject.cpp (revision 51978af14a173e7fae0563b562be5603bc652aeb)
1 // ****************************************************************************
2 //
3 //  	CLayla24DspCommObject.cpp
4 //
5 //		Implementation file for EchoGals generic driver Layla24 DSP
6 //		interface class.
7 //
8 //		Copyright Echo Digital Audio Corporation (c) 1998 - 2002
9 //		All rights reserved
10 //		www.echoaudio.com
11 //
12 //		Permission is hereby granted, free of charge, to any person obtaining a
13 //		copy of this software and associated documentation files (the
14 //		"Software"), to deal with the Software without restriction, including
15 //		without limitation the rights to use, copy, modify, merge, publish,
16 //		distribute, sublicense, and/or sell copies of the Software, and to
17 //		permit persons to whom the Software is furnished to do so, subject to
18 //		the following conditions:
19 //
20 //		- Redistributions of source code must retain the above copyright
21 //		notice, this list of conditions and the following disclaimers.
22 //
23 //		- Redistributions in binary form must reproduce the above copyright
24 //		notice, this list of conditions and the following disclaimers in the
25 //		documentation and/or other materials provided with the distribution.
26 //
27 //		- Neither the name of Echo Digital Audio, nor the names of its
28 //		contributors may be used to endorse or promote products derived from
29 //		this Software without specific prior written permission.
30 //
31 //		THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
32 //		EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
33 //		MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
34 //		IN NO EVENT SHALL THE CONTRIBUTORS OR COPYRIGHT HOLDERS BE LIABLE FOR
35 //		ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
36 //		TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
37 //		SOFTWARE OR THE USE OR OTHER DEALINGS WITH THE SOFTWARE.
38 //
39 // ****************************************************************************
40 
41 #include "CEchoGals.h"
42 #include "CLayla24DspCommObject.h"
43 
44 #include "Layla24DSP.c"		// regular DSP code
45 
46 #include "Layla24_1ASIC.c"
47 #include "Layla24_2A_ASIC.c"
48 #include "Layla24_2S_ASIC.c"
49 
50 //
51 // The ASIC files for Layla24 are always this size
52 //
53 #define LAYLA24_ASIC_SIZE		31146
54 
55 
56 /****************************************************************************
57 
58 	Construction and destruction
59 
60  ****************************************************************************/
61 
62 //===========================================================================
63 //
64 // Constructor
65 //
66 //===========================================================================
67 
68 CLayla24DspCommObject::CLayla24DspCommObject
69 (
70 	PDWORD		pdwRegBase,				// Virtual ptr to DSP registers
71 	PCOsSupport	pOsSupport
72 ) : CGMLDspCommObject( pdwRegBase, pOsSupport )
73 {
74 
75 	strcpy( m_szCardName, "Layla24" );
76 	m_pdwDspRegBase = pdwRegBase;		// Virtual addr DSP's register base
77 
78 	m_wNumPipesOut = 16;
79 	m_wNumPipesIn = 16;
80 	m_wNumBussesOut = 16;
81 	m_wNumBussesIn = 16;
82 	m_wFirstDigitalBusOut = 8;
83 	m_wFirstDigitalBusIn = 8;
84 	m_fHasVmixer = FALSE;
85 
86 	m_wNumMidiOut = 1;					// # MIDI out channels
87 	m_wNumMidiIn = 1;						// # MIDI in  channels
88 	m_bHasASIC = TRUE;
89 
90 	m_pwDspCodeToLoad = pwLayla24DSP;
91 
92 	m_byDigitalMode = DIGITAL_MODE_SPDIF_RCA;
93 	m_bProfessionalSpdif = FALSE;
94 	m_wMtcState = MTC_STATE_NORMAL;
95 
96 }	// CLayla24DspCommObject::CLayla24DspCommObject( DWORD dwPhysRegBase )
97 
98 
99 //===========================================================================
100 //
101 // Destructor
102 //
103 //===========================================================================
104 
105 CLayla24DspCommObject::~CLayla24DspCommObject()
106 {
107 	ECHO_DEBUGPRINTF( ( "CLayla24DspCommObject::~CLayla24DspCommObject() "
108 							  "is toast!\n" ) );
109 }	// CLayla24DspCommObject::~CLayla24DspCommObject()
110 
111 
112 
113 
114 /****************************************************************************
115 
116 	Hardware setup and config
117 
118  ****************************************************************************/
119 
120 //===========================================================================
121 //
122 // Layla24 has an ASIC on the PCI card and another ASIC in the external box;
123 // both need to be loaded.
124 //
125 //===========================================================================
126 
127 BOOL CLayla24DspCommObject::LoadASIC()
128 {
129 	DWORD	dwControlReg;
130 
131 	if ( m_bASICLoaded == TRUE )
132 		return TRUE;
133 
134 	ECHO_DEBUGPRINTF(("CLayla24DspCommObject::LoadASIC\n"));
135 
136 	//
137 	// Give the DSP a few milliseconds to settle down
138 	//
139 	m_pOsSupport->OsSnooze( 10000 );
140 
141 	//
142 	// Load the ASIC for the PCI card
143 	//
144 	if ( !CDspCommObject::LoadASIC( DSP_FNC_LOAD_LAYLA24_PCI_CARD_ASIC,
145 											  pbLayla24_1ASIC,
146 											  sizeof( pbLayla24_1ASIC ) ) )
147 		return FALSE;
148 
149 	m_pbyAsic = pbLayla24_2S_ASIC;
150 
151 	//
152 	// Now give the new ASIC a little time to set up
153 	//
154 	m_pOsSupport->OsSnooze( 10000 );
155 
156 	//
157 	// Do the external one
158 	//
159 	if ( !CDspCommObject::LoadASIC( DSP_FNC_LOAD_LAYLA24_EXTERNAL_ASIC,
160 											  pbLayla24_2S_ASIC,
161 											  sizeof( pbLayla24_2S_ASIC ) ) )
162 		return FALSE;
163 
164 	//
165 	// Now give the external ASIC a little time to set up
166 	//
167 	m_pOsSupport->OsSnooze( 10000 );
168 
169 	//
170 	// See if it worked
171 	//
172 	CheckAsicStatus();
173 
174 	//
175 	// Set up the control register if the load succeeded -
176 	//
177 	// 48 kHz, internal clock, S/PDIF RCA mode
178 	//
179 	if ( m_bASICLoaded )
180 	{
181 		dwControlReg = GML_CONVERTER_ENABLE | GML_48KHZ;
182 		WriteControlReg( dwControlReg );
183 	}
184 
185 	ECHO_DEBUGPRINTF(("\tFinished\n"));
186 
187 	return m_bASICLoaded;
188 }	// BOOL CLayla24DspCommObject::LoadASIC()
189 
190 
191 
192 //===========================================================================
193 //
194 // SetSampleRate
195 //
196 // Set the sample rate for Layla24
197 //
198 // Layla24 is simple; just send it the sampling rate (assuming that the clock
199 // mode is correct).
200 //
201 //===========================================================================
202 
203 DWORD CLayla24DspCommObject::SetSampleRate( DWORD dwNewSampleRate )
204 {
205 	DWORD		dwControlReg, dwNewClock, dwBaseRate;
206 	BOOL		bSetFreqReg = FALSE;
207 
208 	//
209 	// Only set the clock for internal mode.  If the clock is not set to
210 	// internal, try and re-set the input clock; this more transparently
211 	// handles switching between single and double-speed mode
212 	//
213 	if ( GetInputClock() != ECHO_CLOCK_INTERNAL )
214 	{
215 		ECHO_DEBUGPRINTF( ( "CLayla24DspCommObject::SetSampleRate: Cannot set sample rate - "
216 								  "clock not set to CLK_CLOCKININTERNAL\n" ) );
217 
218 		//
219 		// Save the rate anyhow
220 		//
221 		m_pDspCommPage->dwSampleRate = SWAP( dwNewSampleRate );
222 
223 		//
224 		// Set the input clock to the current value
225 		//
226 		SetInputClock( m_wInputClock );
227 
228 		return GetSampleRate();
229 	}
230 
231 	//
232 	// Get the control register & clear the appropriate bits
233 	//
234 	dwControlReg = GetControlRegister();
235 	dwControlReg &= GML_CLOCK_CLEAR_MASK;
236 	dwControlReg &= GML_SPDIF_RATE_CLEAR_MASK;
237 
238 	//
239 	// Set the sample rate
240 	//
241 	bSetFreqReg = FALSE;
242 
243 	switch ( dwNewSampleRate )
244 	{
245 		case 96000 :
246 			dwNewClock = GML_96KHZ;
247 			break;
248 
249 		case 88200 :
250 			dwNewClock = GML_88KHZ;
251 			break;
252 
253 		case 48000 :
254 			dwNewClock = GML_48KHZ | GML_SPDIF_SAMPLE_RATE1;
255 			break;
256 
257 		case 44100 :
258 			dwNewClock = GML_44KHZ;
259 
260 			//
261 			// Professional mode
262 			//
263 			if ( dwControlReg & GML_SPDIF_PRO_MODE )
264 			{
265 				dwNewClock |= GML_SPDIF_SAMPLE_RATE0;
266 			}
267 			break;
268 
269 		case 32000 :
270 			dwNewClock = GML_32KHZ |
271 							 GML_SPDIF_SAMPLE_RATE0 |
272 							 GML_SPDIF_SAMPLE_RATE1;
273 			break;
274 
275 		case 22050 :
276 			dwNewClock = GML_22KHZ;
277 			break;
278 
279 		case 16000 :
280 			dwNewClock = GML_16KHZ;
281 			break;
282 
283 		case 11025 :
284 			dwNewClock = GML_11KHZ;
285 			break;
286 
287 		case 8000 :
288 			dwNewClock = GML_8KHZ;
289 			break;
290 
291 		default :
292 			//
293 			// Set flag to write the frequency register
294 			//
295 			bSetFreqReg = TRUE;
296 
297 			//
298 			// Set for continuous mode
299 			//
300 			dwNewClock = LAYLA24_CONTINUOUS_CLOCK;
301 	}
302 
303 	dwControlReg |= dwNewClock;
304 
305 	//
306 	// If this is a non-standard rate, then the driver
307 	// needs to use Layla24's special "continuous frequency" mode
308 	//
309 	if ( bSetFreqReg )
310 	{
311 		if ( dwNewSampleRate > 50000 )
312 		{
313 			dwBaseRate = dwNewSampleRate >> 1;
314 			dwControlReg |= GML_DOUBLE_SPEED_MODE;
315 		}
316 		else
317 		{
318 			dwBaseRate = dwNewSampleRate;
319 		}
320 
321 		if ( dwBaseRate < 25000 )
322 			dwBaseRate = 25000;
323 
324 		if ( !WaitForHandshake() )
325 			return 0xffffffff;
326 
327 		m_pDspCommPage->dwSampleRate =
328 			SWAP( LAYLA24_MAGIC_NUMBER / dwBaseRate - 2 );
329 
330 		ClearHandshake();
331 		SendVector( DSP_VC_SET_LAYLA24_FREQUENCY_REG );
332 	}
333 
334 	//
335 	// Tell the DSP about it
336 	//
337 	if ( ECHOSTATUS_OK == WriteControlReg( dwControlReg ) )
338 	{
339 		m_pDspCommPage->dwSampleRate = SWAP( dwNewSampleRate );
340 
341 		ECHO_DEBUGPRINTF( ("CLayla24DspCommObject::SetSampleRate: %ld "
342 								 "clock %ld\n", dwNewSampleRate, dwControlReg) );
343 	}
344 
345 	return GetSampleRate();
346 
347 }	// DWORD CLayla24DspCommObject::SetSampleRate( DWORD dwNewSampleRate )
348 
349 
350 //===========================================================================
351 //
352 // SetDigitalMode
353 //
354 //===========================================================================
355 
356 ECHOSTATUS CLayla24DspCommObject::SetDigitalMode
357 (
358 	BYTE	byNewMode
359 )
360 {
361 	DWORD		dwControlReg;
362 
363 	dwControlReg = GetControlRegister();
364 	//
365 	// Clear the current digital mode
366 	//
367 	dwControlReg &= GML_DIGITAL_MODE_CLEAR_MASK;
368 
369 	//
370 	// Tweak the control reg
371 	//
372 	switch ( byNewMode )
373 	{
374 		default :
375 			return ECHOSTATUS_DIGITAL_MODE_NOT_SUPPORTED;
376 
377 		case DIGITAL_MODE_SPDIF_OPTICAL :
378 
379 			dwControlReg |= GML_SPDIF_OPTICAL_MODE;
380 
381 			// fall through
382 
383 		case DIGITAL_MODE_SPDIF_RCA :
384 			//
385 			//	If the input clock is set to ADAT, set the
386 			// input clock to internal and the sample rate to 48 KHz
387 			//
388 			if ( ECHO_CLOCK_ADAT == GetInputClock() )
389 			{
390 				m_pDspCommPage->dwSampleRate = SWAP( (DWORD) 48000 );
391 				SetInputClock( ECHO_CLOCK_INTERNAL );
392 			}
393 			//
394 			// If the currently loaded ASIC is the S/PDIF ASIC, switch
395 			// to the ADAT ASIC
396 			//
397 			if ( !SwitchAsic( pbLayla24_2S_ASIC, sizeof( pbLayla24_2S_ASIC ) ) )
398 				SetInputClock( ECHO_CLOCK_INTERNAL );
399 
400 			break;
401 
402 		case DIGITAL_MODE_ADAT :
403 			//
404 			//	If the input clock is set to S/PDIF, set the
405 			// input clock to internal and the sample rate to 48 KHz
406 			//
407 			if ( ECHO_CLOCK_SPDIF == GetInputClock() )
408 			{
409 				m_pDspCommPage->dwSampleRate = SWAP( (DWORD) 48000 );
410 				SetInputClock( ECHO_CLOCK_INTERNAL );
411 			}
412 			//
413 			// If the currently loaded ASIC is the S/PDIF ASIC, switch
414 			// to the ADAT ASIC
415 			//
416 			if ( !SwitchAsic( pbLayla24_2A_ASIC, sizeof( pbLayla24_2A_ASIC ) ) )
417 				return( ECHOSTATUS_ASIC_NOT_LOADED );
418 
419 			dwControlReg |= GML_ADAT_MODE;
420 			dwControlReg &= ~GML_DOUBLE_SPEED_MODE;
421 			break;
422 	}
423 
424 	//
425 	// Write the control reg
426 	//
427 	WriteControlReg( dwControlReg );
428 
429 	m_byDigitalMode = byNewMode;
430 
431 	ECHO_DEBUGPRINTF( ("CLayla24DspCommObject::SetDigitalMode to %ld\n",
432 							(DWORD) m_byDigitalMode) );
433 
434 	return ECHOSTATUS_OK;
435 
436 }	// ECHOSTATUS CLaya24DspCommObject::SetDigitalMode
437 
438 
439 //===========================================================================
440 //
441 // Depending on what digital mode you want, Layla24 needs different ASICs
442 //	loaded.  This function checks the ASIC needed for the new mode and sees
443 // if it matches the one already loaded.
444 //
445 //===========================================================================
446 
447 BOOL CLayla24DspCommObject::SwitchAsic
448 (
449 	BYTE *	pbyAsicNeeded,
450 	DWORD		dwAsicSize
451 )
452 {
453 	//
454 	//	Check to see if this is already loaded
455 	//
456 	if ( pbyAsicNeeded != m_pbyAsic )
457 	{
458 		BYTE	byMonitors[ MONITOR_ARRAY_SIZE ];
459 		memmove( byMonitors, m_pDspCommPage->byMonitors, MONITOR_ARRAY_SIZE );
460 		memset( m_pDspCommPage->byMonitors,
461 				  GENERIC_TO_DSP(ECHOGAIN_MUTED),
462 				  MONITOR_ARRAY_SIZE );
463 
464 		//
465 		// Load the desired ASIC
466 		//
467 		if ( !CDspCommObject::LoadASIC( DSP_FNC_LOAD_LAYLA24_EXTERNAL_ASIC,
468 												  pbyAsicNeeded,
469 												  dwAsicSize ) )
470 		{
471 			memmove( m_pDspCommPage->byMonitors, byMonitors, MONITOR_ARRAY_SIZE );
472 			return FALSE;
473 		}
474 		m_pbyAsic = pbyAsicNeeded;
475 		memmove( m_pDspCommPage->byMonitors, byMonitors, MONITOR_ARRAY_SIZE );
476 	}
477 
478 	return TRUE;
479 
480 }	// BOOL CLayla24DspCommObject::SwitchAsic( DWORD dwMask96 )
481 
482 
483 //===========================================================================
484 //
485 // SetInputClock
486 //
487 //===========================================================================
488 
489 ECHOSTATUS CLayla24DspCommObject::SetInputClock(WORD wClock)
490 {
491 	BOOL			bSetRate;
492 	BOOL			bWriteControlReg;
493 	DWORD			dwControlReg, dwSampleRate;
494 
495 	ECHO_DEBUGPRINTF( ( "CLayla24DspCommObject::SetInputClock:\n" ) );
496 
497 	dwControlReg = GetControlRegister();
498 
499 	//
500 	// Mask off the clock select bits
501 	//
502 	dwControlReg &= GML_CLOCK_CLEAR_MASK;
503 	dwSampleRate = GetSampleRate();
504 
505 	bSetRate = FALSE;
506 	bWriteControlReg = TRUE;
507 
508 	//
509 	// Pick the new clock
510 	//
511 	switch ( wClock )
512 	{
513 		case ECHO_CLOCK_INTERNAL :
514 			ECHO_DEBUGPRINTF( ( "\tSet Layla24 clock to INTERNAL\n" ) );
515 
516 			// If the sample rate is out of range for some reason, set it
517 			// to a reasonable value.  mattg
518 			if ( ( GetSampleRate() < 8000 ) ||
519 			     ( GetSampleRate() > 100000 ) )
520 			{
521 				m_pDspCommPage->dwSampleRate = SWAP( (DWORD) 48000 );
522 			}
523 
524 			bSetRate = TRUE;
525 			bWriteControlReg = FALSE;
526 			break;
527 
528 		case ECHO_CLOCK_SPDIF:
529 			if ( DIGITAL_MODE_ADAT == GetDigitalMode() )
530 			{
531 				return ECHOSTATUS_CLOCK_NOT_AVAILABLE;
532 			}
533 
534 			ECHO_DEBUGPRINTF( ( "\tSet Layla24 clock to SPDIF\n" ) );
535 
536 			dwControlReg |= GML_SPDIF_CLOCK;
537 
538 /*
539 	Since Layla24 doesn't support 96 kHz S/PDIF, this can be ignored
540 			if ( GML_CLOCK_DETECT_BIT_SPDIF96 & GetInputClockDetect() )
541 			{
542 				dwControlReg |= GML_DOUBLE_SPEED_MODE;
543 			}
544 			else
545 			{
546 				dwControlReg &= ~GML_DOUBLE_SPEED_MODE;
547 			}
548 */
549 			dwControlReg &= ~GML_DOUBLE_SPEED_MODE;
550 			ECHO_DEBUGPRINTF( ( "\tSet Layla24 clock to SPDIF\n" ) );
551 			break;
552 
553 		case ECHO_CLOCK_WORD:
554 			dwControlReg |= GML_WORD_CLOCK;
555 
556 			if ( GML_CLOCK_DETECT_BIT_WORD96 & GetInputClockDetect() )
557 			{
558 				dwControlReg |= GML_DOUBLE_SPEED_MODE;
559 			}
560 			else
561 			{
562 				dwControlReg &= ~GML_DOUBLE_SPEED_MODE;
563 			}
564 			ECHO_DEBUGPRINTF( ( "\tSet Layla24 clock to WORD\n" ) );
565 			break;
566 
567 
568 		case ECHO_CLOCK_ADAT :
569 			if ( DIGITAL_MODE_ADAT != GetDigitalMode() )
570 			{
571 				return ECHOSTATUS_CLOCK_NOT_AVAILABLE;
572 			}
573 
574 			dwControlReg |= GML_ADAT_CLOCK;
575 			dwControlReg &= ~GML_DOUBLE_SPEED_MODE;
576 			ECHO_DEBUGPRINTF( ( "\tSet Layla24 clock to ADAT\n" ) );
577 			break;
578 
579 		default :
580 			ECHO_DEBUGPRINTF(("Input clock 0x%x not supported for Layla24\n",wClock));
581 			ECHO_DEBUGBREAK();
582 				return ECHOSTATUS_CLOCK_NOT_SUPPORTED;
583 
584 	}		// switch (wClock)
585 
586 	//
587 	// Winner! Save the new input clock.
588 	//
589 	m_wInputClock = wClock;
590 
591 	//
592 	// Do things according to the flags
593 	//
594 	if ( bWriteControlReg )
595 	{
596 		WriteControlReg( dwControlReg );
597 	}
598 
599 	//
600 	// If the user just switched to internal clock,
601 	// set the sample rate
602 	//
603 	if ( bSetRate )
604 		SetSampleRate();
605 
606 	return ECHOSTATUS_OK;
607 
608 }		// ECHOSTATUS CLayla24DspCommObject::SetInputClock()
609 
610 
611 
612 //===========================================================================
613 //
614 // Detect MIDI output activity
615 //
616 //===========================================================================
617 
618 BOOL CLayla24DspCommObject::IsMidiOutActive()
619 {
620 	ULONGLONG	ullCurTime;
621 
622 	m_pOsSupport->OsGetSystemTime( &ullCurTime );
623 	return( ( ( ullCurTime - m_ullMidiOutTime ) > MIDI_ACTIVITY_TIMEOUT_USEC ) ? FALSE : TRUE );
624 
625 }	// BOOL CLayla24DspCommObject::IsMidiOutActive()
626 
627 // **** Layla24DspCommObject.cpp ****
628