xref: /haiku/src/add-ons/kernel/drivers/audio/echo/generic/CLayla24DspCommObject.cpp (revision 820dca4df6c7bf955c46e8f6521b9408f50b2900)
1 // ****************************************************************************
2 //
3 //  	CLayla24DspCommObject.cpp
4 //
5 //		Implementation file for EchoGals generic driver Layla24 DSP
6 //		interface class.
7 //
8 // ----------------------------------------------------------------------------
9 //
10 // This file is part of Echo Digital Audio's generic driver library.
11 // Copyright Echo Digital Audio Corporation (c) 1998 - 2005
12 // All rights reserved
13 // www.echoaudio.com
14 //
15 // This library is free software; you can redistribute it and/or
16 // modify it under the terms of the GNU Lesser General Public
17 // License as published by the Free Software Foundation; either
18 // version 2.1 of the License, or (at your option) any later version.
19 //
20 // This library is distributed in the hope that it will be useful,
21 // but WITHOUT ANY WARRANTY; without even the implied warranty of
22 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
23 // Lesser General Public License for more details.
24 //
25 // You should have received a copy of the GNU Lesser General Public
26 // License along with this library; if not, write to the Free Software
27 // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
28 //
29 // ****************************************************************************
30 
31 #include "CEchoGals.h"
32 #include "CLayla24DspCommObject.h"
33 
34 #include LAYLA24_DSP_FILENAME
35 #include "Layla24_1ASIC.c"
36 #include "Layla24_2A_ASIC.c"
37 #include LAYLA24_2ASIC_FILENAME
38 
39 //
40 // The ASIC files for Layla24 are always this size
41 //
42 #define LAYLA24_ASIC_SIZE		31146
43 
44 
45 /****************************************************************************
46 
47 	Construction and destruction
48 
49  ****************************************************************************/
50 
51 //===========================================================================
52 //
53 // Constructor
54 //
55 //===========================================================================
56 
57 CLayla24DspCommObject::CLayla24DspCommObject
58 (
59 	PDWORD		pdwRegBase,				// Virtual ptr to DSP registers
60 	PCOsSupport	pOsSupport
61 ) : CGMLDspCommObject( pdwRegBase, pOsSupport )
62 {
63 
64 	strcpy( m_szCardName, LAYLA24_CARD_NAME);
65 
66 	m_pdwDspRegBase = pdwRegBase;		// Virtual addr DSP's register base
67 
68 	m_wNumPipesOut = 16;
69 	m_wNumPipesIn = 16;
70 	m_wNumBussesOut = 16;
71 	m_wNumBussesIn = 16;
72 	m_wFirstDigitalBusOut = 8;
73 	m_wFirstDigitalBusIn = 8;
74 
75 	m_fHasVmixer = LAYLA24_HAS_VMIXER;
76 
77 	m_wNumMidiOut = 1;					// # MIDI out channels
78 	m_wNumMidiIn = 1;						// # MIDI in  channels
79 	m_bHasASIC = TRUE;
80 
81 	m_pwDspCodeToLoad = LAYLA24_DSP_CODE;
82 
83 	m_byDigitalMode = DIGITAL_MODE_SPDIF_RCA;
84 	m_bProfessionalSpdif = FALSE;
85 	m_wMtcState = MIDI_IN_STATE_NORMAL;
86 
87 	m_dwSampleRate = 48000;
88 
89 }	// CLayla24DspCommObject::CLayla24DspCommObject( DWORD dwPhysRegBase )
90 
91 
92 //===========================================================================
93 //
94 // Destructor
95 //
96 //===========================================================================
97 
98 CLayla24DspCommObject::~CLayla24DspCommObject()
99 {
100 	ECHO_DEBUGPRINTF( ( "CLayla24DspCommObject::~CLayla24DspCommObject() "
101 							  "is toast!\n" ) );
102 }	// CLayla24DspCommObject::~CLayla24DspCommObject()
103 
104 
105 
106 
107 /****************************************************************************
108 
109 	Hardware setup and config
110 
111  ****************************************************************************/
112 
113 //===========================================================================
114 //
115 // Layla24 has an ASIC on the PCI card and another ASIC in the external box;
116 // both need to be loaded.
117 //
118 //===========================================================================
119 
120 BOOL CLayla24DspCommObject::LoadASIC()
121 {
122 	DWORD	dwControlReg;
123 
124 	if ( m_bASICLoaded == TRUE )
125 		return TRUE;
126 
127 	ECHO_DEBUGPRINTF(("CLayla24DspCommObject::LoadASIC\n"));
128 
129 	//
130 	// Give the DSP a few milliseconds to settle down
131 	//
132 	m_pOsSupport->OsSnooze( 10000 );
133 
134 	//
135 	// Load the ASIC for the PCI card
136 	//
137 	if ( !CDspCommObject::LoadASIC( DSP_FNC_LOAD_LAYLA24_PCI_CARD_ASIC,
138 											  pbLayla24_1ASIC,
139 											  sizeof( pbLayla24_1ASIC ) ) )
140 		return FALSE;
141 
142 	m_pbyAsic = pbLayla24_2S_ASIC;
143 
144 	//
145 	// Now give the new ASIC a little time to set up
146 	//
147 	m_pOsSupport->OsSnooze( 10000 );
148 
149 	//
150 	// Do the external one
151 	//
152 	if ( !CDspCommObject::LoadASIC( DSP_FNC_LOAD_LAYLA24_EXTERNAL_ASIC,
153 											  pbLayla24_2S_ASIC,
154 											  sizeof( pbLayla24_2S_ASIC ) ) )
155 		return FALSE;
156 
157 	//
158 	// Now give the external ASIC a little time to set up
159 	//
160 	m_pOsSupport->OsSnooze( 10000 );
161 
162 	//
163 	// See if it worked
164 	//
165 	CheckAsicStatus();
166 
167 	//
168 	// Set up the control register if the load succeeded -
169 	//
170 	// 48 kHz, internal clock, S/PDIF RCA mode
171 	//
172 	if ( m_bASICLoaded )
173 	{
174 		dwControlReg = GML_CONVERTER_ENABLE | GML_48KHZ;
175 		WriteControlReg( dwControlReg, TRUE );
176 
177 		m_dwSampleRate = 48000;
178 	}
179 
180 	ECHO_DEBUGPRINTF(("\tFinished\n"));
181 
182 	return m_bASICLoaded;
183 }	// BOOL CLayla24DspCommObject::LoadASIC()
184 
185 
186 
187 //===========================================================================
188 //
189 // SetSampleRate
190 //
191 // Set the sample rate for Layla24
192 //
193 // Layla24 is simple; just send it the sampling rate (assuming that the clock
194 // mode is correct).
195 //
196 //===========================================================================
197 
198 DWORD CLayla24DspCommObject::SetSampleRate( DWORD dwNewSampleRate )
199 {
200 	DWORD		dwControlReg, dwNewClock, dwBaseRate;
201 	BOOL		bSetFreqReg = FALSE;
202 
203 	//
204 	// Only set the clock for internal mode.  If the clock is not set to
205 	// internal, try and re-set the input clock; this more transparently
206 	// handles switching between single and double-speed mode
207 	//
208 	if ( GetInputClock() != ECHO_CLOCK_INTERNAL )
209 	{
210 		ECHO_DEBUGPRINTF( ( "CLayla24DspCommObject::SetSampleRate: Cannot set sample rate - "
211 								  "clock not set to CLK_CLOCKININTERNAL\n" ) );
212 
213 		//
214 		// Save the rate anyhow
215 		//
216 		m_dwSampleRate = SWAP( dwNewSampleRate );
217 
218 		//
219 		// Set the input clock to the current value
220 		//
221 		SetInputClock( m_wInputClock );
222 
223 		return dwNewSampleRate;
224 	}
225 
226 	//
227 	// Get the control register & clear the appropriate bits
228 	//
229 	dwControlReg = GetControlRegister();
230 	dwControlReg &= GML_CLOCK_CLEAR_MASK;
231 	dwControlReg &= GML_SPDIF_RATE_CLEAR_MASK;
232 
233 	//
234 	// Set the sample rate
235 	//
236 	bSetFreqReg = FALSE;
237 
238 	switch ( dwNewSampleRate )
239 	{
240 		case 96000 :
241 			dwNewClock = GML_96KHZ;
242 			break;
243 
244 		case 88200 :
245 			dwNewClock = GML_88KHZ;
246 			break;
247 
248 		case 48000 :
249 			dwNewClock = GML_48KHZ | GML_SPDIF_SAMPLE_RATE1;
250 			break;
251 
252 		case 44100 :
253 			dwNewClock = GML_44KHZ;
254 
255 			//
256 			// Professional mode
257 			//
258 			if ( dwControlReg & GML_SPDIF_PRO_MODE )
259 			{
260 				dwNewClock |= GML_SPDIF_SAMPLE_RATE0;
261 			}
262 			break;
263 
264 		case 32000 :
265 			dwNewClock = GML_32KHZ |
266 							 GML_SPDIF_SAMPLE_RATE0 |
267 							 GML_SPDIF_SAMPLE_RATE1;
268 			break;
269 
270 		case 22050 :
271 			dwNewClock = GML_22KHZ;
272 			break;
273 
274 		case 16000 :
275 			dwNewClock = GML_16KHZ;
276 			break;
277 
278 		case 11025 :
279 			dwNewClock = GML_11KHZ;
280 			break;
281 
282 		case 8000 :
283 			dwNewClock = GML_8KHZ;
284 			break;
285 
286 		default :
287 			//
288 			// Set flag to write the frequency register
289 			//
290 			bSetFreqReg = TRUE;
291 
292 			//
293 			// Set for continuous mode
294 			//
295 			dwNewClock = LAYLA24_CONTINUOUS_CLOCK;
296 	}
297 
298 	dwControlReg |= dwNewClock;
299 
300 	//
301 	// If this is a non-standard rate, then the driver
302 	// needs to use Layla24's special "continuous frequency" mode
303 	//
304 	if ( bSetFreqReg )
305 	{
306 		if ( dwNewSampleRate > 50000 )
307 		{
308 			dwBaseRate = dwNewSampleRate >> 1;
309 			dwControlReg |= GML_DOUBLE_SPEED_MODE;
310 		}
311 		else
312 		{
313 			dwBaseRate = dwNewSampleRate;
314 		}
315 
316 		if ( dwBaseRate < 25000 )
317 			dwBaseRate = 25000;
318 
319 		if ( !WaitForHandshake() )
320 			return 0xffffffff;
321 
322 		m_pDspCommPage->dwSampleRate =
323 			SWAP( LAYLA24_MAGIC_NUMBER / dwBaseRate - 2 );
324 
325 		ClearHandshake();
326 		SendVector( DSP_VC_SET_LAYLA24_FREQUENCY_REG );
327 	}
328 
329 	//
330 	// Tell the DSP about it
331 	//
332 	if ( ECHOSTATUS_OK == WriteControlReg( dwControlReg ) )
333 	{
334 		m_pDspCommPage->dwSampleRate = SWAP( dwNewSampleRate );
335 
336 		ECHO_DEBUGPRINTF( ("CLayla24DspCommObject::SetSampleRate: %ld "
337 								 "clock %lx\n", dwNewSampleRate, dwControlReg) );
338 	}
339 
340 	m_dwSampleRate = dwNewSampleRate;
341 
342 	return dwNewSampleRate;
343 
344 }	// DWORD CLayla24DspCommObject::SetSampleRate( DWORD dwNewSampleRate )
345 
346 
347 //===========================================================================
348 //
349 // SetDigitalMode
350 //
351 //===========================================================================
352 
353 ECHOSTATUS CLayla24DspCommObject::SetDigitalMode
354 (
355 	BYTE	byNewMode
356 )
357 {
358 	BOOL		AsicOk;
359 
360 	//
361 	// Change the ASIC
362 	//
363 	switch ( byNewMode )
364 	{
365 		case DIGITAL_MODE_SPDIF_RCA :
366 		case DIGITAL_MODE_SPDIF_OPTICAL :
367 			//
368 			// If the currently loaded ASIC is the S/PDIF ASIC, switch
369 			// to the ADAT ASIC
370 			//
371 			AsicOk = SwitchAsic( pbLayla24_2S_ASIC, sizeof( pbLayla24_2S_ASIC ) );
372 			break;
373 
374 		case DIGITAL_MODE_ADAT :
375 			//
376 			// If the currently loaded ASIC is the S/PDIF ASIC, switch
377 			// to the ADAT ASIC
378 			//
379 			AsicOk = SwitchAsic( pbLayla24_2A_ASIC, sizeof( pbLayla24_2A_ASIC ) );
380 			break;
381 
382 		default :
383 			return ECHOSTATUS_DIGITAL_MODE_NOT_SUPPORTED;
384 	}
385 
386 	if (FALSE == AsicOk)
387 		return ECHOSTATUS_ASIC_NOT_LOADED;
388 
389 	//
390 	// Call the base class to tweak the input clock if necessary
391 	//
392 	return CGMLDspCommObject::SetDigitalMode(byNewMode);
393 
394 }	// ECHOSTATUS CLaya24DspCommObject::SetDigitalMode
395 
396 
397 //===========================================================================
398 //
399 // Depending on what digital mode you want, Layla24 needs different ASICs
400 //	loaded.  This function checks the ASIC needed for the new mode and sees
401 // if it matches the one already loaded.
402 //
403 //===========================================================================
404 
405 BOOL CLayla24DspCommObject::SwitchAsic
406 (
407 	BYTE *	pbyAsicNeeded,
408 	DWORD		dwAsicSize
409 )
410 {
411 	BOOL rval;
412 
413 	//
414 	//	Check to see if this is already loaded
415 	//
416 	rval = TRUE;
417 	if ( pbyAsicNeeded != m_pbyAsic )
418 	{
419 		BYTE	byMonitors[ MONITOR_ARRAY_SIZE ];
420 		memmove( byMonitors, m_pDspCommPage->byMonitors, MONITOR_ARRAY_SIZE );
421 		memset( m_pDspCommPage->byMonitors,
422 				  GENERIC_TO_DSP(ECHOGAIN_MUTED),
423 				  MONITOR_ARRAY_SIZE );
424 
425 		//
426 		// Load the desired ASIC
427 		//
428 		rval = CDspCommObject::LoadASIC(DSP_FNC_LOAD_LAYLA24_EXTERNAL_ASIC,
429 												  pbyAsicNeeded,
430 												  dwAsicSize );
431 		if (FALSE != rval)
432 		{
433 			m_pbyAsic = pbyAsicNeeded;
434 		}
435 		memmove( m_pDspCommPage->byMonitors, byMonitors, MONITOR_ARRAY_SIZE );
436 	}
437 
438 	return rval;
439 
440 }	// BOOL CLayla24DspCommObject::SwitchAsic( DWORD dwMask96 )
441 
442 
443 //===========================================================================
444 //
445 // SetInputClock
446 //
447 //===========================================================================
448 
449 ECHOSTATUS CLayla24DspCommObject::SetInputClock(WORD wClock)
450 {
451 	BOOL			bSetRate;
452 	BOOL			bWriteControlReg;
453 	DWORD			dwControlReg, dwSampleRate;
454 
455 	ECHO_DEBUGPRINTF( ( "CLayla24DspCommObject::SetInputClock:\n" ) );
456 
457 	dwControlReg = GetControlRegister();
458 
459 	//
460 	// Mask off the clock select bits
461 	//
462 	dwControlReg &= GML_CLOCK_CLEAR_MASK;
463 	dwSampleRate = GetSampleRate();
464 
465 	bSetRate = FALSE;
466 	bWriteControlReg = TRUE;
467 
468 	//
469 	// Pick the new clock
470 	//
471 	switch ( wClock )
472 	{
473 		case ECHO_CLOCK_INTERNAL :
474 			ECHO_DEBUGPRINTF( ( "\tSet Layla24 clock to INTERNAL\n" ) );
475 
476 			// If the sample rate is out of range for some reason, set it
477 			// to a reasonable value.  mattg
478 			if ( ( GetSampleRate() < 8000 ) ||
479 			     ( GetSampleRate() > 100000 ) )
480 			{
481 				m_pDspCommPage->dwSampleRate = SWAP( (DWORD) 48000 );
482 				m_dwSampleRate = 48000;
483 			}
484 
485 			bSetRate = TRUE;
486 			bWriteControlReg = FALSE;
487 			break;
488 
489 		case ECHO_CLOCK_SPDIF:
490 			if ( DIGITAL_MODE_ADAT == GetDigitalMode() )
491 			{
492 				return ECHOSTATUS_CLOCK_NOT_AVAILABLE;
493 			}
494 
495 			ECHO_DEBUGPRINTF( ( "\tSet Layla24 clock to SPDIF\n" ) );
496 
497 			dwControlReg |= GML_SPDIF_CLOCK;
498 
499 /*
500 	Since Layla24 doesn't support 96 kHz S/PDIF, this can be ignored
501 			if ( GML_CLOCK_DETECT_BIT_SPDIF96 & GetInputClockDetect() )
502 			{
503 				dwControlReg |= GML_DOUBLE_SPEED_MODE;
504 			}
505 			else
506 			{
507 				dwControlReg &= ~GML_DOUBLE_SPEED_MODE;
508 			}
509 */
510 			dwControlReg &= ~GML_DOUBLE_SPEED_MODE;
511 			ECHO_DEBUGPRINTF( ( "\tSet Layla24 clock to SPDIF\n" ) );
512 			break;
513 
514 		case ECHO_CLOCK_WORD:
515 			dwControlReg |= GML_WORD_CLOCK;
516 
517 			if ( GML_CLOCK_DETECT_BIT_WORD96 & GetInputClockDetect() )
518 			{
519 				dwControlReg |= GML_DOUBLE_SPEED_MODE;
520 			}
521 			else
522 			{
523 				dwControlReg &= ~GML_DOUBLE_SPEED_MODE;
524 			}
525 			ECHO_DEBUGPRINTF( ( "\tSet Layla24 clock to WORD\n" ) );
526 			break;
527 
528 
529 		case ECHO_CLOCK_ADAT :
530 			if ( DIGITAL_MODE_ADAT != GetDigitalMode() )
531 			{
532 				return ECHOSTATUS_CLOCK_NOT_AVAILABLE;
533 			}
534 
535 			dwControlReg |= GML_ADAT_CLOCK;
536 			dwControlReg &= ~GML_DOUBLE_SPEED_MODE;
537 			ECHO_DEBUGPRINTF( ( "\tSet Layla24 clock to ADAT\n" ) );
538 			break;
539 
540 		default :
541 			ECHO_DEBUGPRINTF(("Input clock 0x%x not supported for Layla24\n",wClock));
542 			ECHO_DEBUGBREAK();
543 				return ECHOSTATUS_CLOCK_NOT_SUPPORTED;
544 
545 	}		// switch (wClock)
546 
547 	//
548 	// Winner! Save the new input clock.
549 	//
550 	m_wInputClock = wClock;
551 
552 	//
553 	// Do things according to the flags
554 	//
555 	if ( bWriteControlReg )
556 	{
557 		WriteControlReg( dwControlReg, TRUE );
558 	}
559 
560 	//
561 	// If the user just switched to internal clock,
562 	// set the sample rate
563 	//
564 	if ( bSetRate )
565 		SetSampleRate( m_dwSampleRate );
566 
567 	return ECHOSTATUS_OK;
568 
569 }		// ECHOSTATUS CLayla24DspCommObject::SetInputClock()
570 
571 
572 // **** Layla24DspCommObject.cpp ****
573