root/src/add-ons/kernel/drivers/audio/echo/generic/CLaylaDspCommObject.cpp
// ****************************************************************************
//
//      CLaylaDspCommObject.cpp
//
//              Implementation file for EchoGals generic driver Layla 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 "CLaylaDspCommObject.h"

#include "Layla20DSP.c"
#include "LaylaASIC.c"

//
// The ASIC files for Layla20 are always this size
//
#define LAYLA_ASIC_SIZE         32385


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

        Construction and destruction

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

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

CLaylaDspCommObject::CLaylaDspCommObject
(
        PDWORD          pdwRegBase,                             // Virtual ptr to DSP registers
        PCOsSupport     pOsSupport
) : CDspCommObject( pdwRegBase, pOsSupport )
{
        strcpy( m_szCardName, "Layla" );
        m_pdwDspRegBase = pdwRegBase;           // Virtual addr DSP's register base

        m_wNumPipesOut = 12;
        m_wNumPipesIn = 10;
        m_wNumBussesOut = 12;
        m_wNumBussesIn = 10;
        m_wFirstDigitalBusOut = 10;
        m_wFirstDigitalBusIn = 8;

        m_fHasVmixer = FALSE;

        m_wNumMidiOut = 1;                                      // # MIDI out channels
        m_wNumMidiIn = 1;                                               // # MIDI in  channels

        m_bHasASIC = TRUE;

        m_pwDspCodeToLoad = pwLayla20DSP;
        
}       // CLaylaDspCommObject::CLaylaDspCommObject( DWORD dwPhysRegBase )


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

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




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

        Hardware setup and config

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

//===========================================================================
//
// Layla20 has an ASIC in the external box
//
//===========================================================================

BOOL CLaylaDspCommObject::LoadASIC()
{
        if ( m_bASICLoaded == TRUE )
                return TRUE;

        if ( !CDspCommObject::LoadASIC( DSP_FNC_LOAD_LAYLA_ASIC,
                                                                                          pbLaylaASIC,
                                                                                          LAYLA_ASIC_SIZE ) )
                return FALSE;
        //
        // Check if ASIC is alive and well.
        //
        return( CheckAsicStatus() );
}       // BOOL CLaylaDspCommObject::LoadASIC()


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

DWORD CLaylaDspCommObject::SetSampleRate( DWORD dwNewSampleRate )
{
        //
        // Only set the clock for internal mode
        //      Do not return failure, simply treat it as a non-event.
        //
        if ( GetInputClock() != ECHO_CLOCK_INTERNAL )
        {
                ECHO_DEBUGPRINTF( ( "SetSampleRate: Cannot set sample rate because "
                                                                  "Layla clock NOT set to CLK_CLOCKININTERNAL\n" ) );
                m_pDspCommPage->dwSampleRate = SWAP( dwNewSampleRate );
                return GetSampleRate();
        }
        
        //
        // Sanity check - check the sample rate
        //
        if ( ( dwNewSampleRate < 8000 ) ||
                  ( dwNewSampleRate > 50000 ) )
        {
                ECHO_DEBUGPRINTF( ( "SetSampleRate: Layla sample rate %ld out of range, "
                                                                  "no change made\n",
                                                                  dwNewSampleRate) );
                return 0xffffffff;
        }

        if ( !WaitForHandshake() )
                return 0xffffffff;

        m_pDspCommPage->dwSampleRate = SWAP( dwNewSampleRate );
        
        ClearHandshake();
        SendVector( DSP_VC_SET_LAYLA_SAMPLE_RATE );
        ECHO_DEBUGPRINTF( ( "SetSampleRate: Layla sample rate changed to %ld\n",
                                                          dwNewSampleRate ) );
        return( dwNewSampleRate );
}       // DWORD CLaylaDspCommObject::SetSampleRate( DWORD dwNewSampleRate )


//===========================================================================
//
// Send new input clock setting to DSP
//
//===========================================================================

ECHOSTATUS CLaylaDspCommObject::SetInputClock(WORD wClock)
{
        BOOL                    bSetRate;
        WORD                    wNewClock;

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

        bSetRate = FALSE;
        
        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() > 50000 ) )
                        {
                                m_pDspCommPage->dwSampleRate = SWAP( (DWORD) 48000 );
                        }

                        bSetRate = TRUE;
                        wNewClock = LAYLA20_CLOCK_INTERNAL;
                        break;

                case ECHO_CLOCK_SPDIF:
                        ECHO_DEBUGPRINTF( ( "\tSet Layla20 clock to SPDIF\n" ) );
                        
                        wNewClock = LAYLA20_CLOCK_SPDIF;
                        break;

                case ECHO_CLOCK_WORD: 
                        ECHO_DEBUGPRINTF( ( "\tSet Layla20 clock to WORD\n" ) );
                        
                        wNewClock = LAYLA20_CLOCK_WORD;
                        
                        break;

                case ECHO_CLOCK_SUPER: 
                        ECHO_DEBUGPRINTF( ( "\tSet Layla20 clock to SUPER\n" ) );
                        
                        wNewClock = LAYLA20_CLOCK_SUPER;
                        
                        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;

        //
        // Send the new clock to the DSP
        //
        m_pDspCommPage->wInputClock = SWAP(wNewClock);
        ClearHandshake();
        SendVector( DSP_VC_UPDATE_CLOCKS );
        
        if ( bSetRate )
                SetSampleRate();
                
        return ECHOSTATUS_OK;
}               // ECHOSTATUS CLaylaDspCommObject::SetInputClock()


//===========================================================================
//
// Set new output clock
//
//===========================================================================

ECHOSTATUS CLaylaDspCommObject::SetOutputClock(WORD wClock)
{
        WORD wLaylaOutClock;

        if (FALSE == m_bASICLoaded)
                return ECHOSTATUS_ASIC_NOT_LOADED;
                
        if (!WaitForHandshake())
                return ECHOSTATUS_DSP_DEAD;

        ECHO_DEBUGPRINTF( ("CDspCommObject::SetOutputClock:\n") );
                
        //
        // Translate generic driver clock constants to values for Layla20 firmware
        //
        switch (wClock)
        {
                case ECHO_CLOCK_SUPER :
                        wLaylaOutClock = LAYLA20_OUTPUT_CLOCK_SUPER;
                        break;
        
                case ECHO_CLOCK_WORD :
                        wLaylaOutClock = LAYLA20_OUTPUT_CLOCK_WORD;
                        break;
                                                                                                                        
                default :
                        return ECHOSTATUS_INVALID_PARAM;
        }

        m_pDspCommPage->wOutputClock = SWAP(wLaylaOutClock);
        m_wOutputClock = wClock;
        
        ClearHandshake();
        ECHOSTATUS Status = SendVector(DSP_VC_UPDATE_CLOCKS);
        
        return Status;
}       // ECHOSTATUS CLaylaDspCommObject::SetOutputClock


//===========================================================================
//
// Input bus gain - iGain is in units of .5 dB
//
//===========================================================================

ECHOSTATUS CLaylaDspCommObject::SetBusInGain( WORD wBusIn, INT32 iGain)
{
        ECHO_DEBUGPRINTF(("CLaylaDspCommObject::SetBusInGain\n"));
        
        if (wBusIn >= LAYLA20_INPUT_TRIMS)
                return ECHOSTATUS_INVALID_CHANNEL;

        //
        // Store the gain for later use
        //
        m_byInputTrims[wBusIn] = (BYTE) iGain;

        //
        // Adjust the input gain depending on the nominal level switch
        //
        BYTE byMinus10;
        GetNominalLevel( wBusIn + m_wNumBussesOut, &byMinus10);
        
        if (0 == byMinus10)
        {
                //
                // This channel is in +4 mode; subtract 12 dB from the input gain
                // (note that iGain is in units of .5 dB)
                //
                iGain -= 12 << 1;
        }
        
        return CDspCommObject::SetBusInGain(wBusIn,iGain);

}       


ECHOSTATUS CLaylaDspCommObject::GetBusInGain( WORD wBusIn, INT32 &iGain)
{
        if (wBusIn >= LAYLA20_INPUT_TRIMS)
                return ECHOSTATUS_INVALID_CHANNEL;
        
        iGain = (INT32) m_byInputTrims[wBusIn]; 
        
        return ECHOSTATUS_OK;
}       


//===========================================================================
//
// Set the nominal level for an input or output bus
//
//      Set bState to TRUE for -10, FALSE for +4 
//
// Layla20 sets the input nominal level by adjusting the 
// input trim
//
//===========================================================================

ECHOSTATUS CLaylaDspCommObject::SetNominalLevel
(
        WORD    wBus,
        BOOL    bState
)
{
        ECHO_DEBUGPRINTF(("CLaylaDspCommObject::SetNominalLevel\n"));

        if (wBus < m_wNumBussesOut)
        {
                //
                //      This is an output bus; call the base class routine to service it
                //
                return CDspCommObject::SetNominalLevel(wBus,bState);
        }

        //
        // Check the bus number
        //
        ECHO_DEBUGPRINTF(("\tChecking the bus number\n"));
        if (wBus < (m_wNumBussesOut + LAYLA20_INPUT_TRIMS))
        {
                ECHO_DEBUGPRINTF(("\twBus %d  m_pDspCommPage %p\n",wBus,m_pDspCommPage));
                //
                // Set the nominal bit in the comm page
                //              
                if ( bState )
                        m_pDspCommPage->cmdNominalLevel.SetIndexInMask( wBus );
                else
                        m_pDspCommPage->cmdNominalLevel.ClearIndexInMask( wBus );
                
                //
                // Set the input trim, using the current gain
                //      
                ECHO_DEBUGPRINTF(("\tCalling SetBusInGain\n"));
                
                wBus = wBus - m_wNumBussesOut;
                return SetBusInGain(    wBus, (INT32) m_byInputTrims[wBus]);
        }

        ECHO_DEBUGPRINTF( ("CLaylaDspCommObject::SetNominalOutLineLevel Invalid "
                                                         "index %d\n",
                                                         wBus ) );
        return ECHOSTATUS_INVALID_CHANNEL;

}       // ECHOSTATUS CLaylaDspCommObject::SetNominalLevel


//===========================================================================
//
// ASIC status check
//
// This test sometimes fails for Layla20; for Layla20, the loop runs 5 times
// and succeeds if it wins on three of the loops.
//
//===========================================================================

BOOL CLaylaDspCommObject::CheckAsicStatus()
{
        DWORD dwNumTests;
        DWORD dwNumWins;
        BOOL bLoaded;

        dwNumTests = NUM_ASIC_ATTEMPTS;
        dwNumWins = 0;
        do
        {
                bLoaded = CDspCommObject::CheckAsicStatus();
                if (FALSE != bLoaded)
                {
                        dwNumWins++;
                        if (NUM_ASIC_WINS == dwNumWins)
                        {
                                m_bASICLoaded = TRUE;
                                break;
                        }
                }
        
                m_bASICLoaded = FALSE;
                dwNumTests--;
        } while (dwNumTests != 0);

        return m_bASICLoaded;

}       // BOOL CLaylaDspCommObject::CheckAsicStatus()


// **** LaylaDspCommObject.cpp ****