📄 wmxscaledma.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: WMXScaleDMA.c 2924 2006-04-12 12:50:25Z fb $
*
* This file contains XScale specific DMA functionality for the drivers for
* the Wolfson chips.
*
* Warning:
* This driver is specifically written for Wolfson Codecs. It is not a
* general CODEC device driver.
*
* -----------------------------------------------------------------------------*/
/*
* IMPORTANT
*
* The functions in this file must obey two rules:
*
* 1. They do not use any memory other than stack space and parameters passed in.
* 2. They do not make system calls, unless WMSystemCallsAllowed returns TRUE.
*
* All functions take a context parameter, which passes in pointers to the virtual
* addresses of memory-mapped registers, and stores what type of device is being
* accessed.
*
* Some client functions provide shared memory via the v_pWMData member of the
* context structure. This cannot be relied on.
*/
/*
* Include files
*/
#include "WMCommon.h"
#include "WMDevice.h"
#include "WMGlobals.h"
#include "WMAudio.h"
#include "WMAudioInternal.h"
#include "WMPlatformDeviceContext.h"
#include "WMDMAContext.h"
#include "WMVoice.h"
#include "WMDMA.h"
#include "Test/WMTestCommon.h"
#include "WMPlatformInternalTests.h"
#include "WMPlatform_OS.h"
/*
* Only build this if we're doing Audio Streaming support.
*/
#if WM_AUDIO_STREAM
/*
* Private data
*/
/*
* Structure holding the DMA channel corresponding to an API channel.
*/
typedef struct tagAPIToDMAChannelMapping
{
const WMAUDIO_CHANNEL APIChannel;
const XLLP_DMAC_CHANNEL_T DMAChannel;
} APIToDMAChannelMapping;
/*
* And the initial values in the table. Note that the channel numbers will
* be overwritten if we are using dynamic channel allocation.
*/
APIToDMAChannelMapping s_DMAChannelsAvailable [] =
{
{ WMAUDIO_AC97_STEREO_OUT, WMDMA_STEREO_OUT },
{ WMAUDIO_AC97_STEREO_IN, WMDMA_STEREO_IN }
#if WM_MONODAC
,
{ WMAUDIO_AC97_MONO_OUT, WMDMA_MONO_OUT }
#endif
#if WM_I2S
,
{ WMAUDIO_I2S_STEREO_OUT, WMDMA_STEREO_OUT },
{ WMAUDIO_I2S_STEREO_IN, WMDMA_STEREO_IN }
#endif /* WM_I2S */
#if WM_VOICE
,
{ WMAUDIO_VOICE_OUT, WMDMA_VOICE_OUT },
{ WMAUDIO_VOICE_IN, WMDMA_VOICE_IN }
#endif /* WM_VOICE */
};
#ifdef DEBUG
# ifndef WMDMA_DMA_STORE_DEBUG
# define WMDMA_DMA_STORE_DEBUG FALSE
# endif
# if WMDMA_DMA_STORE_DEBUG
/*
* For debugging DMA it can take too long to write out the DMA registers -
* by the time you've written them out, another interrupt has come along and
* you've missed your chance. We solve this by capturing an array of the last
* few sets of registers, and then you can display these periodically, or just
* inspect the values in a debugger.
*
* Note: if you enable this option it can take up a _lot_ of memory.
*/
# define MAX_DMA_DEBUG 10
XLLP_DMAC_T DmacRegsStore[MAX_DMA_DEBUG] = {0};
static int DmaDebugRun = 0;
# endif /* WMDMA_DMA_STORE_DEBUG */
#endif /* DEBUG */
/*
* Global definitions
*/
/*
* Channel flags.
*/
#define WMCHAN_ACTIVE 0x00000001
#define WMCHAN_BUF1_FULL 0x00000010
#define WMCHAN_BUF2_FULL 0x00000020
/*
* A macro to determine whether a DMA buffer is available.
*/
#define IS_BUFFER_AVAILABLE( _pChannelDef, _buf ) \
( ((_pChannelDef)->pDetails->flags & WM_INPUT) ? \
((_pChannelDef)->flags & WMCHAN_ ## _buf ## _FULL ) : \
!((_pChannelDef)->flags & WMCHAN_ ## _buf ## _FULL ) )
/*
* Function prototypes
*/
static WMSTATUS private_PrepareDescriptors( WM_DEVICE_HANDLE hDevice,
ChannelDef *pChannelDef
);
static WMSTATUS private_AllocateChannel( WM_DEVICE_HANDLE hDevice,
ChannelDef *pChannelDef,
WMAUDIO_CHANNEL APIChannel
);
static void private_SetDMABufferLength( WM_DEVICE_HANDLE hDevice,
ChannelDef *pChannelDef,
unsigned int bufsize
);
void private_CleanChannelBuffers( WM_DEVICE_HANDLE hDevice,
ChannelDef *pChannelDef
);
void *private_GetNextBuffer( WM_DEVICE_HANDLE hDevice,
WMDMA_CHAN_HANDLE channel
);
void private_Flush_InputFIFO( WM_DEVICE_HANDLE hDevice,
XLLP_VUINT32_T *pFIFO,
unsigned int depth
);
#ifdef DEBUG
static void private_OutputGivenDmacRegs( WM_DEVICE_HANDLE hDevice,
volatile XLLP_DMAC_T *pDmacRegs
);
static void private_DumpDmacRegs( WM_DEVICE_HANDLE hDevice );
static void private_DumpDmacChannel( WM_DEVICE_HANDLE hDevice,
ChannelDef *pChannelDef
);
static void private_CaptureDmacRegs( WM_DEVICE_HANDLE hDevice );
static void private_DumpCapturedDmacRegs( WM_DEVICE_HANDLE hDevice, int run );
static void private_DumpAllCapturedDmacRegs( WM_DEVICE_HANDLE hDevice );
#else /* DEBUG */
#define private_OutputGivenDmacRegs( _hDevice,_pDmacRegs) (0)
#define private_DumpDmacRegs( _hDevice ) (0)
#define private_DumpDmacChannel( _hDevice, _pChannelDef ) (0)
#define private_CaptureDmacRegs( _hDevice ) (0)
#define private_DumpCapturedDmacRegs( _hDevice, _run ) (0)
#define private_DumpAllCapturedDmacRegs( _hDevice ) (0)
#endif /* DEBUG */
#if WM_TESTING
static WM_BOOL private_TestIsBufferAvailable( WM_DEVICE_HANDLE hDevice );
#endif /* WM_TESTING */
#if WM_USE_DYNAMIC_DMA_CHANNEL
/*
* XllpDmacInit is not exported by any of the xllp header files.
*/
extern XLLP_STATUS_T XllpDmacInit(void);
#endif /* WM_USE_DYNAMIC_DMA_CHANNEL */
/*-----------------------------------------------------------------------------
* Function: WMDMAInit
*
* Prepares the DMA subsystem for use.
*
* Parameters:
* hDevice handle to the device (from WMOpenDevice)
*
* Returns: WMSTATUS
* See WMStatus.h
*---------------------------------------------------------------------------*/
WMSTATUS WMDMAInit( WM_DEVICE_HANDLE hDevice )
{
WMSTATUS status = WMS_SUCCESS;
WM_XSCALE_DEVICE_CONTEXT *pDeviceContext =
(WM_XSCALE_DEVICE_CONTEXT*) hDevice;
DMAContext *pDMAContext = pDeviceContext->pDMAContext;
/*
* Check to make sure that the DMA context is initialised.
*/
WM_ASSERT( hDevice, pDMAContext->initialised );
#if WM_USE_DYNAMIC_DMA_CHANNEL
{
XLLP_STATUS_T xllpStatus;
/*
* If we are using xllp to control the DMA,
* call xllpDmacInit to initialise it.
*/
xllpStatus = XllpDmacInit();
status = XllpStatusToWMStatus( xllpStatus );
}
#endif /* WM_USE_DYNAMIC_DMA_CHANNEL */
return status;
}
/*-----------------------------------------------------------------------------
* Function: WMDMAShutdown
*
* Cleans up the DMA subsystem.
*
* Parameters:
* hDevice handle to the device (from WMOpenDevice)
* channel the channel to clean up.
*
* Returns: void
*---------------------------------------------------------------------------*/
void WMDMAShutdown( WM_DEVICE_HANDLE hDevice )
{
WM_XSCALE_DEVICE_CONTEXT *pDeviceContext =
(WM_XSCALE_DEVICE_CONTEXT*) hDevice;
DMAContext *pDMAContext = pDeviceContext->pDMAContext;
if ( pDMAContext->initialised )
{
pDMAContext->initialised = FALSE;
}
}
/*-----------------------------------------------------------------------------
* Function: WMDMAInitChannel
*
* Prepares the DMA channel for transferring data.
*
* Parameters:
* hDevice handle to the device (from WMOpenDevice)
* APIchannel the channel to prepare.
* bufsize the size of the DMA buffer(s).
*
* Returns: WMDMA_CHAN_HANDLE
* A channel handle to pass in to other functions.
*---------------------------------------------------------------------------*/
WMDMA_CHAN_HANDLE WMDMAInitChannel( WM_DEVICE_HANDLE hDevice,
WMAUDIO_CHANNEL APIChannel,
unsigned int bufsize
)
{
WMSTATUS result;
ChannelDef *pChannelDef;
WM_XSCALE_DEVICE_CONTEXT *pDeviceContext =
(WM_XSCALE_DEVICE_CONTEXT*) hDevice;
DMAContext *pDMAContext = pDeviceContext->pDMAContext;
WM_ASSERT( hDevice, pDMAContext->initialised );
/*
* Get the appropriate channel structure.
*/
pChannelDef = WMDMAGetChannelDef( hDevice, APIChannel );
if ( NULL == pChannelDef )
{
WM_ASSERT( hDevice, (NULL != pChannelDef) );
goto error;
}
/*
* Look up the channel details.
*/
pChannelDef->pDetails = WMDMAGetChannelDetails( hDevice, APIChannel );
if ( NULL == pChannelDef->pDetails )
{
WM_ASSERT( hDevice, (NULL != pChannelDef->pDetails) );
goto error;
}
/*
* Prepare the DMA descriptors.
*/
result = private_PrepareDescriptors( hDevice, pChannelDef );
if ( WM_ERROR( result ) )
{
goto error;
}
/*
* Clean out our buffers.
*/
private_CleanChannelBuffers( hDevice, pChannelDef );
/*
* Set the DMA buffer length.
*/
private_SetDMABufferLength( hDevice, pChannelDef, bufsize );
/*
* And return.
*/
return pChannelDef;
error:
return ( NULL );
}
/*-----------------------------------------------------------------------------
* Function: WMDMAShutdownChannel
*
* Cleans up the given DMA channel.
*
* Parameters:
* hDevice handle to the device (from WMOpenDevice)
* channel the channel to clean up.
*
* Returns: void
*---------------------------------------------------------------------------*/
void WMDMAShutdownChannel( WM_DEVICE_HANDLE hDevice,
WMDMA_CHAN_HANDLE channel
)
{
WMDMAStopChannel( hDevice, channel );
#if WM_USE_DYNAMIC_DMA_CHANNEL
{
ChannelDef *pChannelDef = (ChannelDef *)channel;
XLLP_DMAC_DRCMR_T device;
device = pChannelDef->pDetails->DMADevice;
XllpDmacFreeChannel( pChannelDef->DMAChannel, device );
}
#endif
}
/*-----------------------------------------------------------------------------
* Function: WMDMAGetChannelBuffer
*
* Returns a pointer to the current DMA buffer for the channel.
*
* Parameters:
* hDevice handle to the device (from WMOpenDevice)
* channel the channel to return the buffer for.
*
* Returns: void *
* A pointer to the DMA buffer (of size as configured in WMDMAInit).
*---------------------------------------------------------------------------*/
void *WMDMAGetChannelBuffer( WM_DEVICE_HANDLE hDevice,
WMDMA_CHAN_HANDLE channel
)
{
ChannelDef *pChannelDef = (ChannelDef *)channel;
void *pBuffer = NULL;
WM_XSCALE_DEVICE_CONTEXT *pDeviceContext =
(WM_XSCALE_DEVICE_CONTEXT*) hDevice;
/*
* If neither buffer is available, we can't give them a buffer.
*/
if ( !IS_BUFFER_AVAILABLE( pChannelDef, BUF1 ) &&
!IS_BUFFER_AVAILABLE( pChannelDef, BUF2 )
)
{
pBuffer = NULL;
goto end;
}
/*
* Default to the next DMA buffer.
*/
pBuffer = private_GetNextBuffer( hDevice, channel );
/*
* If not available, try the other one. That should be available
* because we checked above that at least one was available.
*/
if ( pChannelDef->buffer1 == pBuffer &&
!IS_BUFFER_AVAILABLE( pChannelDef, BUF1 )
)
{
pBuffer = pChannelDef->buffer2;
}
else if ( pChannelDef->buffer2 == pBuffer &&
!IS_BUFFER_AVAILABLE( pChannelDef, BUF2 )
)
{
pBuffer = pChannelDef->buffer1;
}
end:
return pBuffer;
}
/*-----------------------------------------------------------------------------
* Function: WMDMAMarkBufferFull
*
* Marks the given DMA buffer as full and awaiting transfer.
*
* Parameters:
* hDevice handle to the device (from WMOpenDevice)
* channel the channel ID.
* pBuffer the buffer to mark as full (from WMDMAGetChannelBuffer).
*
* Returns: void
*---------------------------------------------------------------------------*/
void WMDMAMarkBufferFull( WM_DEVICE_HANDLE hDevice,
WMDMA_CHAN_HANDLE channel,
void *pBuffer
)
{
ChannelDef *pChannelDef = (ChannelDef *)channel;
WM_XSCALE_DEVICE_CONTEXT *pDeviceContext =
(WM_XSCALE_DEVICE_CONTEXT*) hDevice;
if ( pChannelDef->buffer1 == pBuffer )
{
pChannelDef->flags |= WMCHAN_BUF1_FULL;
}
else if ( pChannelDef->buffer2 == pBuffer )
{
pChannelDef->flags |= WMCHAN_BUF2_FULL;
}
else
{
WM_ASSERT( hDevice,
pChannelDef->buffer1 == pBuffer || pChannelDef->buffer2 == pBuffer
);
}
/*
* If the channel is not active then start it.
* The buffer must be full because this function has been called.
*/
if( !( pChannelDef->flags & WMCHAN_ACTIVE ) )
{
WMDMAStartChannel( hDevice, channel );
}
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -