⛏️ index : haiku.git

// ****************************************************************************
//
//  	CLayla24DspCommObject.cpp
//
//		Implementation file for EchoGals generic driver Layla24 DSP
//		interface class.
//
// ----------------------------------------------------------------------------
//
// This file is part of Echo Digital Audio's generic driver library.
// Copyright Echo Digital Audio Corporation (c) 1998 - 2005
// All rights reserved
// www.echoaudio.com
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; either
// version 2.1 of the License, or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
//
// ****************************************************************************

#include "CEchoGals.h"
#include "CLayla24DspCommObject.h"

#include LAYLA24_DSP_FILENAME
#include "Layla24_1ASIC.c"
#include "Layla24_2A_ASIC.c"
#include LAYLA24_2ASIC_FILENAME

//
// The ASIC files for Layla24 are always this size
//
#define LAYLA24_ASIC_SIZE		31146


/****************************************************************************

	Construction and destruction

 ****************************************************************************/

//===========================================================================
//
// Constructor
//
//===========================================================================

CLayla24DspCommObject::CLayla24DspCommObject
(
	PDWORD		pdwRegBase,				// Virtual ptr to DSP registers
	PCOsSupport	pOsSupport
) : CGMLDspCommObject( pdwRegBase, pOsSupport )
{

	strcpy( m_szCardName, LAYLA24_CARD_NAME);

	m_pdwDspRegBase = pdwRegBase;		// Virtual addr DSP's register base
	
	m_wNumPipesOut = 16;
	m_wNumPipesIn = 16;
	m_wNumBussesOut = 16;
	m_wNumBussesIn = 16;
	m_wFirstDigitalBusOut = 8;
	m_wFirstDigitalBusIn = 8;

	m_fHasVmixer = LAYLA24_HAS_VMIXER;

	m_wNumMidiOut = 1;					// # MIDI out channels
	m_wNumMidiIn = 1;						// # MIDI in  channels
	m_bHasASIC = TRUE;
	
	m_pwDspCodeToLoad = LAYLA24_DSP_CODE;

	m_byDigitalMode = DIGITAL_MODE_SPDIF_RCA;
	m_bProfessionalSpdif = FALSE;
	m_wMtcState = MIDI_IN_STATE_NORMAL;
	
	m_dwSampleRate = 48000;
	
}	// CLayla24DspCommObject::CLayla24DspCommObject( DWORD dwPhysRegBase )


//===========================================================================
//
// Destructor
//
//===========================================================================

CLayla24DspCommObject::~CLayla24DspCommObject()
{
	ECHO_DEBUGPRINTF( ( "CLayla24DspCommObject::~CLayla24DspCommObject() "
							  "is toast!\n" ) );
}	// CLayla24DspCommObject::~CLayla24DspCommObject()




/****************************************************************************

	Hardware setup and config

 ****************************************************************************/

//===========================================================================
//
// Layla24 has an ASIC on the PCI card and another ASIC in the external box; 
// both need to be loaded.
//
//===========================================================================

BOOL CLayla24DspCommObject::LoadASIC()
{
	DWORD	dwControlReg;

	if ( m_bASICLoaded == TRUE )
		return TRUE;
		
	ECHO_DEBUGPRINTF(("CLayla24DspCommObject::LoadASIC\n"));

	//
	// Give the DSP a few milliseconds to settle down
	//
	m_pOsSupport->OsSnooze( 10000 );

	//
	// Load the ASIC for the PCI card
	//
	if ( !CDspCommObject::LoadASIC( DSP_FNC_LOAD_LAYLA24_PCI_CARD_ASIC,
											  pbLayla24_1ASIC,
											  sizeof( pbLayla24_1ASIC ) ) )
		return FALSE;

	m_pbyAsic = pbLayla24_2S_ASIC;	

	//
	// Now give the new ASIC a little time to set up
	//
	m_pOsSupport->OsSnooze( 10000 );

	//
	// Do the external one
	//
	if ( !CDspCommObject::LoadASIC( DSP_FNC_LOAD_LAYLA24_EXTERNAL_ASIC,
											  pbLayla24_2S_ASIC,
											  sizeof( pbLayla24_2S_ASIC ) ) )
		return FALSE;

	//
	// Now give the external ASIC a little time to set up
	//
	m_pOsSupport->OsSnooze( 10000 );
		
	//
	// See if it worked
	//
	CheckAsicStatus();

	//
	// Set up the control register if the load succeeded -
	//
	// 48 kHz, internal clock, S/PDIF RCA mode
	//	
	if ( m_bASICLoaded )
	{
		dwControlReg = GML_CONVERTER_ENABLE | GML_48KHZ;
		WriteControlReg( dwControlReg, TRUE );	
		
		m_dwSampleRate = 48000;
	}

	ECHO_DEBUGPRINTF(("\tFinished\n"));
		
	return m_bASICLoaded;
}	// BOOL CLayla24DspCommObject::LoadASIC()



//===========================================================================
//
// SetSampleRate
// 
// Set the sample rate for Layla24
//
// Layla24 is simple; just send it the sampling rate (assuming that the clock
// mode is correct).
//
//===========================================================================

DWORD CLayla24DspCommObject::SetSampleRate( DWORD dwNewSampleRate )
{
	DWORD		dwControlReg, dwNewClock, dwBaseRate;
	BOOL		bSetFreqReg = FALSE;
	
	//
	// Only set the clock for internal mode.  If the clock is not set to
	// internal, try and re-set the input clock; this more transparently
	// handles switching between single and double-speed mode
	//
	if ( GetInputClock() != ECHO_CLOCK_INTERNAL )
	{
		ECHO_DEBUGPRINTF( ( "CLayla24DspCommObject::SetSampleRate: Cannot set sample rate - "
								  "clock not set to CLK_CLOCKININTERNAL\n" ) );
		
		//
		// Save the rate anyhow
		//
		m_dwSampleRate = SWAP( dwNewSampleRate );
		
		//
		// Set the input clock to the current value
		//
		SetInputClock( m_wInputClock );
		
		return dwNewSampleRate;
	}

	//
	// Get the control register & clear the appropriate bits
	// 
	dwControlReg = GetControlRegister();
	dwControlReg &= GML_CLOCK_CLEAR_MASK;
	dwControlReg &= GML_SPDIF_RATE_CLEAR_MASK;
	
	//
	// Set the sample rate
	//
	bSetFreqReg = FALSE;

	switch ( dwNewSampleRate )
	{
		case 96000 :
			dwNewClock = GML_96KHZ;
			break;
		
		case 88200 :
			dwNewClock = GML_88KHZ;
			break;
		
		case 48000 : 
			dwNewClock = GML_48KHZ | GML_SPDIF_SAMPLE_RATE1;
			break;
		
		case 44100 : 
			dwNewClock = GML_44KHZ;

			//
			// Professional mode
			//
			if ( dwControlReg & GML_SPDIF_PRO_MODE )
			{
				dwNewClock |= GML_SPDIF_SAMPLE_RATE0;
			}
			break;
		
		case 32000 :
			dwNewClock = GML_32KHZ | 
							 GML_SPDIF_SAMPLE_RATE0 | 
							 GML_SPDIF_SAMPLE_RATE1;
			break;
		
		case 22050 :
			dwNewClock = GML_22KHZ;
			break;
		
		case 16000 :
			dwNewClock = GML_16KHZ;
			break;
		
		case 11025 :
			dwNewClock = GML_11KHZ;
			break;
		
		case 8000 :
			dwNewClock = GML_8KHZ;
			break;

		default :
			//
			// Set flag to write the frequency register
			//
			bSetFreqReg = TRUE;

			//
			// Set for continuous mode
			//
			dwNewClock = LAYLA24_CONTINUOUS_CLOCK;
	}

	dwControlReg |= dwNewClock;

	//
	// If this is a non-standard rate, then the driver
	// needs to use Layla24's special "continuous frequency" mode
	//
	if ( bSetFreqReg )
	{
		if ( dwNewSampleRate > 50000 )
		{
			dwBaseRate = dwNewSampleRate >> 1;
			dwControlReg |= GML_DOUBLE_SPEED_MODE;
		}
		else
		{
			dwBaseRate = dwNewSampleRate;
		}
		
		if ( dwBaseRate < 25000 )
			dwBaseRate = 25000;

		if ( !WaitForHandshake() )
			return 0xffffffff;

		m_pDspCommPage->dwSampleRate = 
			SWAP( LAYLA24_MAGIC_NUMBER / dwBaseRate - 2 );

		ClearHandshake();			
		SendVector( DSP_VC_SET_LAYLA24_FREQUENCY_REG );
	}
	
	//
	// Tell the DSP about it
	//
	if ( ECHOSTATUS_OK == WriteControlReg( dwControlReg ) )
	{
		m_pDspCommPage->dwSampleRate = SWAP( dwNewSampleRate );

		ECHO_DEBUGPRINTF( ("CLayla24DspCommObject::SetSampleRate: %ld "
								 "clock %lx\n", dwNewSampleRate, dwControlReg) );
	}
	
	m_dwSampleRate = dwNewSampleRate;

	return dwNewSampleRate;

}	// DWORD CLayla24DspCommObject::SetSampleRate( DWORD dwNewSampleRate )


//===========================================================================
//
// SetDigitalMode
// 
//===========================================================================

ECHOSTATUS CLayla24DspCommObject::SetDigitalMode
(
	BYTE	byNewMode
)
{
	BOOL		AsicOk;
	
	//
	// Change the ASIC
	//
	switch ( byNewMode )
	{
		case DIGITAL_MODE_SPDIF_RCA :
		case DIGITAL_MODE_SPDIF_OPTICAL :
			//
			// If the currently loaded ASIC is the S/PDIF ASIC, switch
			// to the ADAT ASIC
			//
			AsicOk = SwitchAsic( pbLayla24_2S_ASIC, sizeof( pbLayla24_2S_ASIC ) );
			break;
			
		case DIGITAL_MODE_ADAT :
			//
			// If the currently loaded ASIC is the S/PDIF ASIC, switch
			// to the ADAT ASIC
			//
			AsicOk = SwitchAsic( pbLayla24_2A_ASIC, sizeof( pbLayla24_2A_ASIC ) );
			break;	

		default :
			return ECHOSTATUS_DIGITAL_MODE_NOT_SUPPORTED;
	}
	
	if (FALSE == AsicOk)
		return ECHOSTATUS_ASIC_NOT_LOADED;

	//
	// Call the base class to tweak the input clock if necessary
	//
	return CGMLDspCommObject::SetDigitalMode(byNewMode);

}	// ECHOSTATUS CLaya24DspCommObject::SetDigitalMode


//===========================================================================
//
// Depending on what digital mode you want, Layla24 needs different ASICs 
//	loaded.  This function checks the ASIC needed for the new mode and sees 
// if it matches the one already loaded.
//
//===========================================================================

BOOL CLayla24DspCommObject::SwitchAsic
(
	BYTE *	pbyAsicNeeded,
	DWORD		dwAsicSize
)
{
	BOOL rval;

	//
	//	Check to see if this is already loaded
	// 
	rval = TRUE;
	if ( pbyAsicNeeded != m_pbyAsic )
	{
		BYTE	byMonitors[ MONITOR_ARRAY_SIZE ];
		memmove( byMonitors, m_pDspCommPage->byMonitors, MONITOR_ARRAY_SIZE );
		memset( m_pDspCommPage->byMonitors,
				  GENERIC_TO_DSP(ECHOGAIN_MUTED),
				  MONITOR_ARRAY_SIZE );

		//
		// Load the desired ASIC
		//
		rval = CDspCommObject::LoadASIC(DSP_FNC_LOAD_LAYLA24_EXTERNAL_ASIC,
												  pbyAsicNeeded,
												  dwAsicSize );
		if (FALSE != rval)
		{
			m_pbyAsic = pbyAsicNeeded;	
		}
		memmove( m_pDspCommPage->byMonitors, byMonitors, MONITOR_ARRAY_SIZE );
	}
	
	return rval;

}	// BOOL CLayla24DspCommObject::SwitchAsic( DWORD dwMask96 )


//===========================================================================
//
// SetInputClock
//
//===========================================================================

ECHOSTATUS CLayla24DspCommObject::SetInputClock(WORD wClock)
{
	BOOL			bSetRate;
	BOOL			bWriteControlReg;
	DWORD			dwControlReg, dwSampleRate;

	ECHO_DEBUGPRINTF( ( "CLayla24DspCommObject::SetInputClock:\n" ) );

	dwControlReg = GetControlRegister();

	//
	// Mask off the clock select bits
	//
	dwControlReg &= GML_CLOCK_CLEAR_MASK;
	dwSampleRate = GetSampleRate();
	
	bSetRate = FALSE;
	bWriteControlReg = TRUE;

	//
	// Pick the new clock
	//
	switch ( wClock )
	{
		case ECHO_CLOCK_INTERNAL : 
			ECHO_DEBUGPRINTF( ( "\tSet Layla24 clock to INTERNAL\n" ) );

			// If the sample rate is out of range for some reason, set it
			// to a reasonable value.  mattg
			if ( ( GetSampleRate() < 8000 ) ||
			     ( GetSampleRate() > 100000 ) )
			{
				m_pDspCommPage->dwSampleRate = SWAP( (DWORD) 48000 );
				m_dwSampleRate = 48000;
			}

			bSetRate = TRUE;
			bWriteControlReg = FALSE;
			break;

		case ECHO_CLOCK_SPDIF:
			if ( DIGITAL_MODE_ADAT == GetDigitalMode() )
			{
				return ECHOSTATUS_CLOCK_NOT_AVAILABLE;
			}
			
			ECHO_DEBUGPRINTF( ( "\tSet Layla24 clock to SPDIF\n" ) );
	
			dwControlReg |= GML_SPDIF_CLOCK;

/* 
	Since Layla24 doesn't support 96 kHz S/PDIF, this can be ignored
			if ( GML_CLOCK_DETECT_BIT_SPDIF96 & GetInputClockDetect() )
			{
				dwControlReg |= GML_DOUBLE_SPEED_MODE;
			}
			else
			{
				dwControlReg &= ~GML_DOUBLE_SPEED_MODE;
			}
*/
			dwControlReg &= ~GML_DOUBLE_SPEED_MODE;
			ECHO_DEBUGPRINTF( ( "\tSet Layla24 clock to SPDIF\n" ) );
			break;

		case ECHO_CLOCK_WORD: 
			dwControlReg |= GML_WORD_CLOCK;
			
			if ( GML_CLOCK_DETECT_BIT_WORD96 & GetInputClockDetect() )
			{
				dwControlReg |= GML_DOUBLE_SPEED_MODE;
			}
			else
			{
				dwControlReg &= ~GML_DOUBLE_SPEED_MODE;
			}
			ECHO_DEBUGPRINTF( ( "\tSet Layla24 clock to WORD\n" ) );
			break;


		case ECHO_CLOCK_ADAT :
			if ( DIGITAL_MODE_ADAT != GetDigitalMode() )
			{
				return ECHOSTATUS_CLOCK_NOT_AVAILABLE;
			}
			
			dwControlReg |= GML_ADAT_CLOCK;
			dwControlReg &= ~GML_DOUBLE_SPEED_MODE;
			ECHO_DEBUGPRINTF( ( "\tSet Layla24 clock to ADAT\n" ) );
			break;

		default :
			ECHO_DEBUGPRINTF(("Input clock 0x%x not supported for Layla24\n",wClock));
			ECHO_DEBUGBREAK();
				return ECHOSTATUS_CLOCK_NOT_SUPPORTED;

	}		// switch (wClock)

	//
	// Winner! Save the new input clock.
	//
	m_wInputClock = wClock;

	//
	// Do things according to the flags
	//
	if ( bWriteControlReg )
	{
		WriteControlReg( dwControlReg, TRUE );
	}
	
	//
	// If the user just switched to internal clock,
	// set the sample rate
	//
	if ( bSetRate )
		SetSampleRate( m_dwSampleRate );

	return ECHOSTATUS_OK;

}		// ECHOSTATUS CLayla24DspCommObject::SetInputClock()


// **** Layla24DspCommObject.cpp ****