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

#include "MonaDSP.c"
#include "Mona361DSP.c"

#include "Mona1ASIC48.c"
#include "Mona1ASIC96.c"
#include "Mona1ASIC48_361.c"
#include "Mona1ASIC96_361.c"
#include "Mona2ASIC.c"


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

        Construction and destruction

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

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

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

        m_wNumPipesOut = 14;
        m_wNumPipesIn = 12;
        m_wNumBussesOut = 14;
        m_wNumBussesIn = 12;
        m_wFirstDigitalBusOut = 6;
        m_wFirstDigitalBusIn = 4;
        
        m_bProfessionalSpdif = FALSE;

        m_fHasVmixer = FALSE;   

        m_wNumMidiOut = 0;                                      // # MIDI out channels
        m_wNumMidiIn = 0;                                               // # MIDI in  channels
        m_pDspCommPage->dwSampleRate = SWAP( (DWORD) 44100 );
                                                                                                // Need this in cse we start with ESYNC
        m_bHasASIC = TRUE;
        if ( DEVICE_ID_56361 == pOsSupport->GetDeviceId() )
                m_pwDspCodeToLoad = pwMona361DSP;
        else
                m_pwDspCodeToLoad = pwMonaDSP;

        m_byDigitalMode = DIGITAL_MODE_SPDIF_RCA;
}       // CMonaDspCommObject::CMonaDspCommObject( DWORD dwPhysRegBase )


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

CMonaDspCommObject::~CMonaDspCommObject()
{
}       // CMonaDspCommObject::~CMonaDspCommObject()




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

        Hardware setup and config

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

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

BOOL CMonaDspCommObject::LoadASIC()
{
        DWORD   dwControlReg;
        PBYTE   pbAsic1;
        DWORD   dwSize;

        if ( m_bASICLoaded )
                return TRUE;

        m_pOsSupport->OsSnooze( 10000 );

        if ( DEVICE_ID_56361 == m_pOsSupport->GetDeviceId() )
        {
                pbAsic1 = pbMona1ASIC48_361;
                dwSize = sizeof( pbMona1ASIC48_361 );
        }
        else
        {
                pbAsic1 = pbMona1ASIC48;
                dwSize = sizeof( pbMona1ASIC48 );
        }
        if ( !CDspCommObject::LoadASIC( DSP_FNC_LOAD_MONA_PCI_CARD_ASIC,
                                                                                          pbAsic1,
                                                                                          dwSize ) )
                return FALSE;

        m_pbyAsic = pbAsic1;    

        m_pOsSupport->OsSnooze( 10000 );

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

        m_pOsSupport->OsSnooze( 10000 );
                
        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;
                ECHO_DEBUGPRINTF(("CMonaDspCommObject::LoadASIC - setting control reg for 0x%lx\n",
                                                                dwControlReg));
                WriteControlReg( dwControlReg, TRUE );  
        }
                
        return m_bASICLoaded;
        
}       // BOOL CMonaDspCommObject::LoadASIC()


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

BOOL CMonaDspCommObject::SwitchAsic( DWORD dwMask96 )
{
        BYTE *  pbyAsicNeeded;
        DWORD           dwAsicSize;
        
        //
        //      Check the clock detect bits to see if this is
        // a single-speed clock or a double-speed clock; load
        // a new ASIC if necessary.
        // 
        if ( DEVICE_ID_56361 == m_pOsSupport->GetDeviceId() )
        {
                pbyAsicNeeded = pbMona1ASIC48_361;
                dwAsicSize = sizeof( pbMona1ASIC48_361 );
                if ( 0 != ( dwMask96 & GetInputClockDetect() ) )
                {
                        pbyAsicNeeded = pbMona1ASIC96_361;
                        dwAsicSize = sizeof( pbMona1ASIC96_361 );
                }
        }
        else
        {
                pbyAsicNeeded = pbMona1ASIC48;
                dwAsicSize = sizeof( pbMona1ASIC48 );
                if ( 0 != ( dwMask96 & GetInputClockDetect() ) )
                {
                        pbyAsicNeeded = pbMona1ASIC96;
                        dwAsicSize = sizeof( pbMona1ASIC96 );
                }
        }

        if ( pbyAsicNeeded != m_pbyAsic )
        {
                //
                // Load the desired ASIC
                //
                if ( !CDspCommObject::LoadASIC( DSP_FNC_LOAD_MONA_PCI_CARD_ASIC,
                                                                                                  pbyAsicNeeded,
                                                                                                  dwAsicSize ) )
                        return FALSE;

                m_pbyAsic = pbyAsicNeeded;      
                
                m_pDspCommPage->dwSampleRate = SWAP( (DWORD) 48000 );
        }
        
        return TRUE;

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


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

ECHOSTATUS CMonaDspCommObject::SetInputClock(WORD wClock)
{
        BOOL                    bSetRate;
        BOOL                    bWriteControlReg;
        DWORD                   dwControlReg;

        ECHO_DEBUGPRINTF( ("CMonaDspCommObject::SetInputClock: clock %d\n",wClock) );

        dwControlReg = GetControlRegister();

        //
        // Mask off the clock select bits
        //
        dwControlReg &= GML_CLOCK_CLEAR_MASK;
        
        bSetRate = FALSE;
        bWriteControlReg = TRUE;
        switch ( wClock )
        {
                case ECHO_CLOCK_INTERNAL : 
                {
                        ECHO_DEBUGPRINTF( ( "\tSet Mona clock to INTERNAL\n" ) );
        
                        bSetRate = TRUE;
                        bWriteControlReg = FALSE;

                        break;
                } // CLK_CLOCKININTERNAL

                case ECHO_CLOCK_SPDIF :
                {
                        if ( DIGITAL_MODE_ADAT == GetDigitalMode() )
                        {
                                return ECHOSTATUS_CLOCK_NOT_AVAILABLE;
                        }

                        if ( FALSE == SwitchAsic( GML_CLOCK_DETECT_BIT_SPDIF96 ) )
                        {
                                return ECHOSTATUS_CLOCK_NOT_AVAILABLE;
                        }
                        
                        ECHO_DEBUGPRINTF( ( "\tSet Mona clock to SPDIF\n" ) );
        
                        dwControlReg |= GML_SPDIF_CLOCK;

                        if ( GML_CLOCK_DETECT_BIT_SPDIF96 & GetInputClockDetect() )
                        {
                                dwControlReg |= GML_DOUBLE_SPEED_MODE;
                        }
                        else
                        {
                                dwControlReg &= ~GML_DOUBLE_SPEED_MODE;
                        }
                        break;
                } // CLK_CLOCKINSPDIF

                case ECHO_CLOCK_WORD : 
                {
                        ECHO_DEBUGPRINTF( ( "\tSet Mona clock to WORD\n" ) );

                        if ( FALSE == SwitchAsic( GML_CLOCK_DETECT_BIT_WORD96 ) )
                        {
                                return ECHOSTATUS_CLOCK_NOT_AVAILABLE;
                        }
                
                        dwControlReg |= GML_WORD_CLOCK;
                        
                        if ( GML_CLOCK_DETECT_BIT_WORD96 & GetInputClockDetect() )
                        {
                                dwControlReg |= GML_DOUBLE_SPEED_MODE;
                        }
                        else
                        {
                                dwControlReg &= ~GML_DOUBLE_SPEED_MODE;
                        }
                        break;
                } // CLK_CLOCKINWORD

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

                default :
                        ECHO_DEBUGPRINTF(("Input clock 0x%x not supported for Mona\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 );
        }

        // Set Mona sample rate to something sane if word or superword is
        // being turned off
        if ( bSetRate )
        {
                SetSampleRate( GetSampleRate() );
        }
                
        return ECHOSTATUS_OK;
        
}       // ECHOSTATUS CMonaDspCommObject::SetInputClock



//===========================================================================
//
// SetSampleRate
// 
// Set the audio sample rate for CMona
//
//===========================================================================

DWORD CMonaDspCommObject::SetSampleRate( DWORD dwNewSampleRate )
{
        BYTE *pbyAsicNeeded;
        DWORD   dwAsicSize, dwControlReg, dwNewClock;
        BOOL    fForceControlReg;
        
        ECHO_DEBUGPRINTF(("CMonaDspCommObject::SetSampleRate to %ld\n",dwNewSampleRate));
        
        fForceControlReg = 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( ( "CMonaDspCommObject::SetSampleRate: Cannot set sample rate - "
                                                                  "clock not set to CLK_CLOCKININTERNAL\n" ) );
                
                //
                // Save the rate anyhow
                //
                m_pDspCommPage->dwSampleRate = SWAP( dwNewSampleRate );
                
                //
                // Set the input clock to the current value
                //
                SetInputClock( m_wInputClock );
                
                return GetSampleRate();
        }
        
        //
        // Now, check to see if the required ASIC is loaded
        //
        if ( dwNewSampleRate >= 88200 )
        {
                if ( DIGITAL_MODE_ADAT == GetDigitalMode() )
                        return( GetSampleRate() );

                if ( DEVICE_ID_56361 == m_pOsSupport->GetDeviceId() )
                {
                        pbyAsicNeeded = pbMona1ASIC96_361;
                        dwAsicSize = sizeof(pbMona1ASIC96_361);
                }
                else
                {
                        pbyAsicNeeded = pbMona1ASIC96;
                        dwAsicSize = sizeof(pbMona1ASIC96);
                }
        }
        else
        {
                if ( DEVICE_ID_56361 == m_pOsSupport->GetDeviceId() )
                {
                        pbyAsicNeeded = pbMona1ASIC48_361;
                        dwAsicSize = sizeof(pbMona1ASIC48_361);
                }
                else
                {
                        pbyAsicNeeded = pbMona1ASIC48;
                        dwAsicSize = sizeof(pbMona1ASIC48);
                }
        }

        if ( pbyAsicNeeded != m_pbyAsic )
        {
                ECHO_DEBUGPRINTF(("\tLoading a new ASIC\n"));
                //
                // Load the desired ASIC
                //
                if ( FALSE == CDspCommObject::LoadASIC
                                                                                                        ( DSP_FNC_LOAD_MONA_PCI_CARD_ASIC,
                                                                                                          pbyAsicNeeded,
                                                                                                          dwAsicSize ) )
                        return( GetSampleRate() );

                m_pbyAsic = pbyAsicNeeded;      
                
                fForceControlReg = TRUE;
        }

        //
        // Get the new control register value
        //
        dwNewClock = 0;

        dwControlReg = GetControlRegister();
        dwControlReg &= GML_CLOCK_CLEAR_MASK;
        dwControlReg &= GML_SPDIF_RATE_CLEAR_MASK;

        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;
        }

        dwControlReg |= dwNewClock;

        //
        // Send the new value to the card
        //
        if ( ECHOSTATUS_OK == WriteControlReg( dwControlReg, fForceControlReg ) )
        {
                m_pDspCommPage->dwSampleRate = SWAP( dwNewSampleRate );

                ECHO_DEBUGPRINTF( ("CMonaDspCommObject::SetSampleRate: %ld "
                                                                 "clock %ld\n", dwNewSampleRate, dwNewClock) );
        }

        return GetSampleRate();

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


//===========================================================================
//
//      Set digital mode
//
//===========================================================================

ECHOSTATUS CMonaDspCommObject::SetDigitalMode
(
        BYTE    byNewMode
)
{
        ECHO_DEBUGPRINTF(("CMonaDspCommObject::SetDigitalMode %d\n",byNewMode));

        //
        // If the new mode is ADAT mode, make sure that the single speed ASIC is loaded
        //
        BYTE *pbAsic96;
        
        if (DIGITAL_MODE_ADAT == byNewMode)
        {
                switch (m_pOsSupport->GetDeviceId())
                {
                        case DEVICE_ID_56301 :
                                pbAsic96 = pbMona1ASIC96;
                                break;
                                
                        case DEVICE_ID_56361 :
                                pbAsic96 = pbMona1ASIC96_361;
                                break;
                                
                        default :                               // should never happen, but it's good to cover all the bases
                                return ECHOSTATUS_BAD_CARDID;
                }
                if (pbAsic96 == m_pbyAsic)
                        SetSampleRate( 48000 ); 
        }
        
        //
        // Call the base class to tweak the input clock if necessary
        //
        return CGMLDspCommObject::SetDigitalMode(byNewMode);
        
}       // ECHOSTATUS CMonaDspCommObject::SetDigitalMode


// **** CMonaDspCommObject.cpp ****