📄 wm97power.c
字号:
/*-----------------------------------------------------------------------------
* Copyright (c) Wolfson Microelectronics plc. All rights reserved.
*
* This software as well as any related documentation is furnished under
* license and may only be used or copied in accordance with the terms of the
* license. The information in this file is furnished for informational use
* only, is subject to change without notice, and should not be construed as
* a commitment by Wolfson Microelectronics plc. Wolfson Microelectronics plc
* assumes no responsibility or liability for any errors or inaccuracies that
* may appear in this document or any software that may be provided in
* association with this document.
*
* Except as permitted by such license, no part of this document may be
* reproduced, stored in a retrieval system, or transmitted in any form or by
* any means without the express written consent of Wolfson Microelectronics plc.
*
* $Id: WM97Power.c 4176 2006-10-17 07:50:59Z ian $
*
* This file contains functionality for controlling power on AC97 codecs.
*
* Warning:
* This driver is specifically written for Wolfson Codecs. It is not a
* general CODEC device driver.
*
* -----------------------------------------------------------------------------*/
/*
* Include files
*/
#include "WMCommon.h"
#include "WMDevice.h"
#include "WMControlLink.h"
#include "WMPlatformACLink.h"
#include "WMACLink.h"
#include "WMPower.h"
#include "WM97Power.h"
/*
* Global definitions
*/
/*
* Touch panel masks.
*/
/*
* Private data
*/
/*
* Only provide these if we're supporting Wolfson AC97 devices.
*/
#if WM_AC97
/*
* Function prototypes
*/
static WM_REGVAL private_CalcPdwValue( WM_DEVICE_HANDLE hDevice,
WM_POWERFLAG powerSections
);
static WM_POWERFLAG private_CalcTouchPower( WM_DEVICE_HANDLE hDevice );
/*-----------------------------------------------------------------------------
* Function: WM97PowerInit
*
* Initalise the driver in a low power state.
*
* Parameters:
* hDevice handle to the device (from WMOpenDevice)
* driverId The device ID (e.g. WM_DRIVER_TOUCH)
*
* Returns: WMSTATUS
* See WMStatus.h.
*---------------------------------------------------------------------------*/
WMSTATUS WM97PowerInit( WM_DEVICE_HANDLE hDevice,
WM_DRIVER_ID driverId
)
{
WMSTATUS status;
#if !WM_ZERO_DATA_DAC_SWITCH_ENABLE
/*
* Very quiet samples which swing from total silence to
* a small noise floor can cause problems as the stereo DAC
* mutes output only for for the truly silent sections.
* If this causes a problem the following code should cure it.
*/
if ( IS_WM9705_FAMILY( hDevice ) ||
IS_WM9712_FAMILY( hDevice ) ||
( IS_WM9713_FAMILY( hDevice ) && WM_REVISION_EQUALS( hDevice, WM_REV_A ) )
)
{
status = WMClearField( hDevice,
WM97_ADD_FUN_CONTROL,
WM97_ADDFUN_AMUTE
);
if ( WM_ERROR( status ) )
{
WM_TRACE( hDevice,
( "WMPowerUp - couldn't disable automute: %s",
WMStatusText( status ) ) );
/*
* If this failed, something's gone seriously wrong - we can't read
* or we can't write - so there's no point trying to continue.
*/
goto error;
}
}
/*
* When the DAC is operating at a sample rate other than 48KHz
* it will switch to 48 khz when enough silence is played.
*/
if ( IS_WM9713_FAMILY( hDevice ) && WM_REVISION_EQUALS( hDevice, WM_REV_A ) )
{
status = WMSetField( hDevice, WM9713_RESERVED_1,
WM9713_ZERO_DATA_SWITCH_DISABLE,
WM9713_ZERO_DATA_SWITCH_DISABLE );
if ( WM_ERROR( status ) )
{
WM_TRACE( hDevice,
( "WMPowerUp - couldn't disable zero data switch: %s",
WMStatusText( status ) ) );
/*
* If this failed, something's gone seriously wrong - we can't read
* or we can't write - so there's no point trying to continue.
*/
goto error;
}
}
else if ( IS_WM9712_FAMILY( hDevice ) )
{
status = WMSetField( hDevice, WM9712_ADD_FUN2,
WM9712_ZERO_DATA_SWITCH_DISABLE,
WM9712_ZERO_DATA_SWITCH_DISABLE );
if ( WM_ERROR( status ) )
{
WM_TRACE( hDevice,
( "WMPowerUp - couldn't disable zero data switch: %s",
WMStatusText( status ) ) );
/*
* If this failed, something's gone seriously wrong - we can't read
* or we can't write - so there's no point trying to continue.
*/
goto error;
}
}
#endif /* WM_ZERO_DATA_DAC_SWITCH_ENABLE */
/*
* For all other AC97 device, except the WM9713 family, that start powered
* up, power down the audio section of the device.
*/
if ( !IS_WM9713_FAMILY( hDevice ) )
{
status = WM97PowerDown( hDevice, driverId, WM_POWER_AUDIO );
if ( WM_ERROR( status ) )
{
goto error;
}
}
else
{
/*
* To reduce pops and clicks on the outputs always
* leave them powered up but leave the mixer to outputs at Vmid
* until we need it. This also requires the 1 Meg Vmid resistor
* string to be powered up.
*
* Enable the thermal sensor to protect
* the outputs from becoming too hot.
*
*/
/*
* Power up PR3 (VREF)in reg 0x26
*/
status = WMClearField( hDevice, WM97_POWER_CONTROL, WM97_PWR_VREF );
if ( WM_ERROR( status ) )
{
goto error;
}
/*
* Power up VREF, the 1 Meg Vmid resistor string and thermal shutdown bits
* in register 0x3C
*/
status = WMClearField( hDevice,
WM9713_EXT_POWERDOWN_1,
( WM9713_EXTPWR_VREF |
WM9713_EXTPWR_VMID1M |
WM9713_EXTPWR_THERMALSHUT
)
);
if ( WM_ERROR( status ) )
{
goto error;
}
/*
* Enable fast power up of outputs.
*/
status = WMSetField( hDevice,
WM9713_FAST_POWER_CONTROL,
( WM9713_FAST_POWER_OUT4_ENABLE |
WM9713_FAST_POWER_OUT3_ENABLE |
WM9713_FAST_POWER_HPR_ENABLE |
WM9713_FAST_POWER_HPL_ENABLE |
WM9713_FAST_POWER_SPKR_ENABLE |
WM9713_FAST_POWER_SPKL_ENABLE |
WM9713_FAST_POWER_MONO_ENABLE ),
( WM9713_FAST_POWER_OUT4_ENABLE |
WM9713_FAST_POWER_OUT3_ENABLE |
WM9713_FAST_POWER_HPR_ENABLE |
WM9713_FAST_POWER_HPL_ENABLE |
WM9713_FAST_POWER_SPKR_ENABLE |
WM9713_FAST_POWER_SPKL_ENABLE |
WM9713_FAST_POWER_MONO_ENABLE )
);
/*
* Power up PR6 (Ouputs) in reg 0x26
*/
status = WMClearField( hDevice, WM97_POWER_CONTROL, WM97_PWR_OUTPUTS );
if ( WM_ERROR( status ) )
{
goto error;
}
/*
* Power up the output PGAs in register 0x3E.
*/
status = WMClearField( hDevice,
WM9713_EXT_POWERDOWN_2,
( WM9713_EXTPWR_SPEAKER_RIGHT |
WM9713_EXTPWR_SPEAKER_LEFT |
WM9713_EXTPWR_HEADPHONE_RIGHT |
WM9713_EXTPWR_HEADPHONE_LEFT |
WM9713_EXTPWR_OUT3 |
WM9713_EXTPWR_OUT4 |
WM9713_EXTPWR_MONOOUT )
);
if ( WM_ERROR( status ) )
{
goto error;
}
}
return WMS_SUCCESS;
error:
return status;
}
/*-----------------------------------------------------------------------------
* Function: WM97PowerUp
*
* Called to power up the specific sections of the chip on behalf of this
* driver.
*
* Parameters:
* hDevice handle to the device (from WMOpenDevice)
* driverId The device ID (e.g. WM_DRIVER_TOUCH)
* powerSections The sections to power up.
*
* Returns: WMSTATUS
* See WMStatus.h.
*---------------------------------------------------------------------------*/
WMSTATUS WM97PowerUp( WM_DEVICE_HANDLE hDevice,
WM_DRIVER_ID driverId,
WM_POWERFLAG powerSections
)
{
#if WM_CACHE_POWER_STATE
WM_DEVICE_CONTEXT *pDeviceContext = WMHANDLE_TO_DEVICE( hDevice );
#endif /* WM_CACHE_POWER_STATE */
WMSTATUS status;
WM_POWERFLAG newPowered = 0; /* What should be powered when we finish */
WM_POWERFLAG powerUp; /* What should be but isn't yet */
WM_REGVAL powerReg;
WM_POWERFLAG currentPower;
/*
* Work out what we need powered on.
* newPowered is what will be powered on at the end.
* powerUp is what will be powered up which isn't currently.
*
* Note: there are things which need to be done even if the codec is
* powered up, since some codecs start up with everything on. In this
* case we don't need to power anything up, but we still need to do
* other things such as enabling on-board amplifiers (for example).
*/
newPowered = calcPowered( hDevice, driverId, powerSections, 0, ¤tPower );
powerUp = newPowered & ~currentPower;
/*
* If the AC Link's not powered up, enable the controller to use the AC link
* and do a warm reset to start it up.
*/
if ( !(currentPower & WM_POWER_LINK) )
{
WMPlatformACLinkEnableController( hDevice );
status = WMWake( hDevice );
if ( WM_ERROR( status ) )
{
goto error;
}
}
/*
* Get the current reading if necessary.
*/
#if WM_CACHE_POWER_STATE
if ( pDeviceContext->v_pWMData &&
(pDeviceContext->v_pWMData->flags & WM_POWERREG_VALID)
)
{
powerReg = pDeviceContext->v_pWMData->powerReg[PWR_REG_1];
}
else
#endif /* WM_CACHE_POWER_STATE */
{
status = WMRead( hDevice, WM97_POWER_CONTROL, &powerReg );
if ( WM_ERROR( status ) )
{
goto error;
}
#if WM_CACHE_POWER_STATE
if ( pDeviceContext->v_pWMData )
{
pDeviceContext->v_pWMData->flags |= WM_POWERREG_VALID;
}
#endif /* WM_CACHE_POWER_STATE */
}
/*
* Enable audio.
* Recommended power-up sequence is
* PR5, PR3, PR2, PR1, PR0, PR6
* (PR4 is automatic on wake-up)
*
* Note: we use newPowered in this test (instead of powerUp)
* because there are things we may still need to do if the
* audio starts up powered up.
* We do not want to do any of this if we are only powering up the
* touch part of the device.
*/
if ( ( newPowered & WM_POWER_AUDIO ) && ( powerUp & ~WM_POWER_TOUCH ) )
{
/*
* The 9712 defaults to no ramp-down - turn it on (clear it) to
* avoid clicks on muting, powerup/down, etc.
*/
if ( IS_WM9712_FAMILY( hDevice ) )
{
status = WMClearField( hDevice, WM9712_ADD_FUN2, WM9712_ADDFUN_RAMPDOWN );
if ( WM_ERROR( status ) )
{
WM_TRACE( hDevice,
( "WMPowerUp - couldn't enable rampdown: %s",
WMStatusText( status ) ) );
/*
* If this failed, something's gone seriously wrong - we can't read
* or we can't write - so there's no point trying to continue.
*/
goto error;
}
}
/* Turn on the clock (PR5). */
if ( powerUp & WM_POWER_CLOCK )
{
powerReg &= ~WM97_PWR_CLOCK;
status = WMWrite( hDevice, WM97_POWER_CONTROL, powerReg );
if ( WM_ERROR( status ) )
{
goto error;
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -