Back to home page

LXR

 
 

    


File indexing completed on 2025-05-11 08:23:00

0001 /*
0002  * Copyright 2019 NXP
0003  * All rights reserved.
0004  *
0005  * SPDX-License-Identifier: BSD-3-Clause
0006  */
0007 
0008 #include "fsl_pdm_edma.h"
0009 
0010 /* Component ID definition, used by tools. */
0011 #ifndef FSL_COMPONENT_ID
0012 #define FSL_COMPONENT_ID "platform.drivers.pdm_edma"
0013 #endif
0014 
0015 /*******************************************************************************
0016  * Definitations
0017  ******************************************************************************/
0018 /* Used for 32byte aligned */
0019 #define STCD_ADDR(address) (edma_tcd_t *)(((uint32_t)(address) + 32) & ~0x1FU)
0020 
0021 /*<! Structure definition for pdm_edma_private_handle_t. The structure is private. */
0022 typedef struct _pdm_edma_private_handle
0023 {
0024     PDM_Type *base;
0025     pdm_edma_handle_t *handle;
0026 } pdm_edma_private_handle_t;
0027 
0028 /*! @brief pdm transfer state */
0029 enum _pdm_edma_transfer_state
0030 {
0031     kPDM_Busy = 0x0U, /*!< PDM is busy */
0032     kPDM_Idle,        /*!< Transfer is done. */
0033 };
0034 
0035 /*******************************************************************************
0036  * Prototypes
0037  ******************************************************************************/
0038 /*!
0039  * @brief PDM EDMA callback for receive.
0040  *
0041  * @param handle pointer to pdm_edma_handle_t structure which stores the transfer state.
0042  * @param userData Parameter for user callback.
0043  * @param done If the DMA transfer finished.
0044  * @param tcds The TCD index.
0045  */
0046 static void PDM_EDMACallback(edma_handle_t *handle, void *userData, bool done, uint32_t tcds);
0047 
0048 /*!
0049  * @brief Mapping the enabled channel to a number power of 2.
0050  *
0051  * @param channel PDM channel number.
0052  */
0053 static edma_modulo_t PDM_TransferMappingChannel(uint32_t *channel);
0054 /*******************************************************************************
0055  * Variables
0056  ******************************************************************************/
0057 
0058 /*! @brief pdm base address pointer */
0059 static PDM_Type *const s_pdmBases[] = PDM_BASE_PTRS;
0060 /*<! Private handle only used for internally. */
0061 static pdm_edma_private_handle_t s_edmaPrivateHandle[ARRAY_SIZE(s_pdmBases)];
0062 /*******************************************************************************
0063  * Code
0064  ******************************************************************************/
0065 static edma_modulo_t PDM_TransferMappingChannel(uint32_t *channel)
0066 {
0067     edma_modulo_t modulo = kEDMA_ModuloDisable;
0068 #if FSL_FEATURE_PDM_CHANNEL_NUM == 8U
0069     if (*channel == 2U)
0070     {
0071         modulo = kEDMA_Modulo8bytes;
0072     }
0073     else if ((*channel == 3U) || (*channel == 4U))
0074     {
0075         *channel = 4U;
0076         modulo   = kEDMA_Modulo16bytes;
0077     }
0078     else
0079     {
0080         modulo = kEDMA_ModuloDisable;
0081     }
0082 #endif
0083 
0084     return modulo;
0085 }
0086 
0087 static void PDM_EDMACallback(edma_handle_t *handle, void *userData, bool done, uint32_t tcds)
0088 {
0089     pdm_edma_private_handle_t *privHandle = (pdm_edma_private_handle_t *)userData;
0090     pdm_edma_handle_t *pdmHandle          = privHandle->handle;
0091 
0092     if (!(pdmHandle->isLoopTransfer))
0093     {
0094         (void)memset(&pdmHandle->tcd[pdmHandle->tcdDriver], 0, sizeof(edma_tcd_t));
0095         pdmHandle->tcdDriver = (pdmHandle->tcdDriver + 1U) % pdmHandle->tcdNum;
0096     }
0097 
0098     pdmHandle->receivedBytes +=
0099         pdmHandle->tcd[pdmHandle->tcdDriver].BITER * (pdmHandle->tcd[pdmHandle->tcdDriver].NBYTES & 0x3FFU);
0100 
0101     /* If finished a block, call the callback function */
0102     if (pdmHandle->callback != NULL)
0103     {
0104         (pdmHandle->callback)(privHandle->base, pdmHandle, kStatus_PDM_Idle, pdmHandle->userData);
0105     }
0106 
0107     pdmHandle->tcdUsedNum--;
0108     /* If all data finished, just stop the transfer */
0109     if ((pdmHandle->tcdUsedNum == 0U) && !(pdmHandle->isLoopTransfer))
0110     {
0111         /* Disable DMA enable bit */
0112         PDM_EnableDMA(privHandle->base, false);
0113         EDMA_AbortTransfer(handle);
0114     }
0115 }
0116 
0117 /*!
0118  * brief Initializes the PDM Rx eDMA handle.
0119  *
0120  * This function initializes the PDM slave DMA handle, which can be used for other PDM master transactional APIs.
0121  * Usually, for a specified PDM instance, call this API once to get the initialized handle.
0122  *
0123  * param base PDM base pointer.
0124  * param handle PDM eDMA handle pointer.
0125  * param base PDM peripheral base address.
0126  * param callback Pointer to user callback function.
0127  * param userData User parameter passed to the callback function.
0128  * param dmaHandle eDMA handle pointer, this handle shall be static allocated by users.
0129  */
0130 void PDM_TransferCreateHandleEDMA(
0131     PDM_Type *base, pdm_edma_handle_t *handle, pdm_edma_callback_t callback, void *userData, edma_handle_t *dmaHandle)
0132 {
0133     assert((handle != NULL) && (dmaHandle != NULL));
0134 
0135     uint32_t instance = PDM_GetInstance(base);
0136 
0137     /* Zero the handle */
0138     (void)memset(handle, 0, sizeof(*handle));
0139 
0140     /* Set pdm base to handle */
0141     handle->dmaHandle = dmaHandle;
0142     handle->callback  = callback;
0143     handle->userData  = userData;
0144 
0145     /* Set PDM state to idle */
0146     handle->state = (uint32_t)kPDM_Idle;
0147 
0148     s_edmaPrivateHandle[instance].base   = base;
0149     s_edmaPrivateHandle[instance].handle = handle;
0150 
0151     /* Install callback for Tx dma channel */
0152     EDMA_SetCallback(dmaHandle, PDM_EDMACallback, &s_edmaPrivateHandle[instance]);
0153 }
0154 
0155 /*!
0156  * brief Initializes the multi PDM channel interleave type.
0157  *
0158  * This function initializes the PDM DMA handle member interleaveType, it shall be called only when application would
0159  * like to use type kPDM_EDMAMultiChannelInterleavePerChannelBlock, since the default interleaveType is
0160  * kPDM_EDMAMultiChannelInterleavePerChannelSample always
0161  *
0162  * param handle PDM eDMA handle pointer.
0163  * param multiChannelInterleaveType Multi channel interleave type.
0164  */
0165 void PDM_TransferSetMultiChannelInterleaveType(pdm_edma_handle_t *handle,
0166                                                pdm_edma_multi_channel_interleave_t multiChannelInterleaveType)
0167 {
0168     handle->interleaveType = multiChannelInterleaveType;
0169 }
0170 
0171 /*!
0172  * brief Install EDMA descriptor memory.
0173  *
0174  * param handle Pointer to EDMA channel transfer handle.
0175  * param tcdAddr EDMA head descriptor address.
0176  * param tcdNum EDMA link descriptor address.
0177  */
0178 void PDM_TransferInstallEDMATCDMemory(pdm_edma_handle_t *handle, void *tcdAddr, size_t tcdNum)
0179 {
0180     assert(handle != NULL);
0181 
0182     handle->tcd    = (edma_tcd_t *)tcdAddr;
0183     handle->tcdNum = tcdNum;
0184 }
0185 
0186 /*!
0187  * brief Configures the PDM channel.
0188  *
0189  * param base PDM base pointer.
0190  * param handle PDM eDMA handle pointer.
0191  * param channel channel index.
0192  * param pdmConfig pdm channel configurations.
0193  */
0194 void PDM_TransferSetChannelConfigEDMA(PDM_Type *base,
0195                                       pdm_edma_handle_t *handle,
0196                                       uint32_t channel,
0197                                       const pdm_channel_config_t *config)
0198 {
0199     assert((handle != NULL) && (config != NULL));
0200     assert(channel < (uint32_t)FSL_FEATURE_PDM_CHANNEL_NUM);
0201 
0202     /* Configure the PDM channel */
0203     PDM_SetChannelConfig(base, channel, config);
0204 
0205     /* record end channel number */
0206     handle->endChannel = (uint8_t)channel;
0207     /* increase totoal enabled channel number */
0208     handle->channelNums++;
0209     /* increase count pre channel numbers */
0210     handle->count = (uint8_t)(base->FIFO_CTRL & PDM_FIFO_CTRL_FIFOWMK_MASK);
0211 }
0212 
0213 /*!
0214  * brief Performs a non-blocking PDM receive using eDMA.
0215  *
0216  * note This interface returns immediately after the transfer initiates. Call
0217  * the PDM_GetReceiveRemainingBytes to poll the transfer status and check whether the PDM transfer is finished.
0218  *
0219  * 1. Scatter gather case:
0220  * This functio support dynamic scatter gather and staic scatter gather,
0221  * a. for the dynamic scatter gather case:
0222  * Application should call PDM_TransferReceiveEDMA function continuously to make sure new receive request is submit
0223  *before the previous one finish. b. for the static scatter gather case: Application should use the link transfer
0224  *feature and make sure a loop link transfer is provided, such as: code pdm_edma_transfer_t pdmXfer[2] =
0225  *   {
0226  *       {
0227  *       .data  = s_buffer,
0228  *       .dataSize = BUFFER_SIZE,
0229  *       .linkTransfer = &pdmXfer[1],
0230  *       },
0231  *
0232  *       {
0233  *       .data  = &s_buffer[BUFFER_SIZE],
0234  *       .dataSize = BUFFER_SIZE,
0235  *       .linkTransfer = &pdmXfer[0]
0236  *       },
0237  *   };
0238  *endcode
0239  *
0240  * 2. Multi channel case:
0241  * This function support receive multi pdm channel data, for example, if two channel is requested,
0242  * code
0243  * PDM_TransferSetChannelConfigEDMA(DEMO_PDM, &s_pdmRxHandle_0, DEMO_PDM_ENABLE_CHANNEL_0, &channelConfig);
0244  * PDM_TransferSetChannelConfigEDMA(DEMO_PDM, &s_pdmRxHandle_0, DEMO_PDM_ENABLE_CHANNEL_1, &channelConfig);
0245  * PDM_TransferReceiveEDMA(DEMO_PDM, &s_pdmRxHandle_0, pdmXfer);
0246  * endcode
0247  * The output data will be formatted as below if handle->interleaveType =
0248  *kPDM_EDMAMultiChannelInterleavePerChannelSample :
0249  * -------------------------------------------------------------------------
0250  * |CHANNEL0 | CHANNEL1 | CHANNEL0 | CHANNEL1 | CHANNEL0 | CHANNEL 1 | ....|
0251  * -------------------------------------------------------------------------
0252  *
0253  * The output data will be formatted as below if handle->interleaveType = kPDM_EDMAMultiChannelInterleavePerChannelBlock
0254  *:
0255  * ----------------------------------------------------------------------------------------------------------------------
0256  * |CHANNEL3 | CHANNEL3 | CHANNEL3 | .... | CHANNEL4 | CHANNEL 4 | CHANNEL4 |....| CHANNEL5 | CHANNEL 5 | CHANNEL5
0257  *|....|
0258  * ----------------------------------------------------------------------------------------------------------------------
0259  * Note: the dataSize of xfer is the total data size, while application using
0260  * kPDM_EDMAMultiChannelInterleavePerChannelBlock, the buffer size for each PDM channel is channelSize = dataSize /
0261  * channelNums, there are limitation for this feature,
0262  * 1. For 3 DMIC array: the dataSize shall be 4 * (channelSize)
0263  * The addtional buffer is mandantory for edma modulo feature.
0264  * 2. The kPDM_EDMAMultiChannelInterleavePerChannelBlock feature support below dmic array only,
0265  *    2 DMIC array: CHANNEL3, CHANNEL4
0266  *    3 DMIC array: CHANNEL3, CHANNEL4, CHANNEL5
0267  *    4 DMIC array: CHANNEL3, CHANNEL4, CHANNEL5, CHANNEL6
0268  * Any other combinations is not support, that is to SAY, THE FEATURE SUPPORT RECEIVE START FROM CHANNEL3 ONLY AND 4
0269  * MAXIMUM DMIC CHANNELS.
0270  *
0271  * param base PDM base pointer
0272  * param handle PDM eDMA handle pointer.
0273  * param xfer Pointer to DMA transfer structure.
0274  * retval kStatus_Success Start a PDM eDMA receive successfully.
0275  * retval kStatus_InvalidArgument The input argument is invalid.
0276  * retval kStatus_RxBusy PDM is busy receiving data.
0277  */
0278 status_t PDM_TransferReceiveEDMA(PDM_Type *base, pdm_edma_handle_t *handle, pdm_edma_transfer_t *xfer)
0279 {
0280     assert((handle != NULL) && (xfer != NULL));
0281 
0282     edma_transfer_config_t config = {0};
0283     uint32_t startAddr            = PDM_GetDataRegisterAddress(base, handle->endChannel - (handle->channelNums - 1UL));
0284     pdm_edma_transfer_t *currentTransfer = xfer;
0285     uint32_t nextTcdIndex = 0U, tcdIndex = handle->tcdUser, destOffset = FSL_FEATURE_PDM_FIFO_WIDTH;
0286     uint32_t mappedChannel = handle->channelNums;
0287     edma_modulo_t modulo   = kEDMA_ModuloDisable;
0288     /* minor offset used for channel sample interleave transfer */
0289     edma_minor_offset_config_t minorOffset = {
0290         .enableSrcMinorOffset  = true,
0291         .enableDestMinorOffset = false,
0292         .minorOffset           = 0xFFFFFU - mappedChannel * (uint32_t)FSL_FEATURE_PDM_FIFO_OFFSET + 1U};
0293 
0294     /* Check if input parameter invalid */
0295     if ((xfer->data == NULL) || (xfer->dataSize == 0U))
0296     {
0297         return kStatus_InvalidArgument;
0298     }
0299 
0300     if ((handle->interleaveType == kPDM_EDMAMultiChannelInterleavePerChannelBlock) && (mappedChannel > 1U))
0301     {
0302         /* Limitation of the feature, reference the API comments */
0303         if (((startAddr & 0xFU) != 0U) || (mappedChannel > 4U))
0304         {
0305             return kStatus_InvalidArgument;
0306         }
0307         modulo = PDM_TransferMappingChannel(&mappedChannel);
0308         if ((xfer->dataSize % mappedChannel) != 0U)
0309         {
0310             return kStatus_InvalidArgument;
0311         }
0312         destOffset = xfer->dataSize / mappedChannel;
0313         /* reconfigure the minor loop offset for channel block interleave */
0314         minorOffset.enableSrcMinorOffset = false, minorOffset.enableDestMinorOffset = true,
0315         minorOffset.minorOffset =
0316             0xFFFFFU - mappedChannel * (uint32_t)destOffset + (uint32_t)FSL_FEATURE_PDM_FIFO_WIDTH + 1U;
0317     }
0318 
0319     while (currentTransfer != NULL)
0320     {
0321         if (handle->tcdUsedNum >= handle->tcdNum)
0322         {
0323             return kStatus_PDM_QueueFull;
0324         }
0325         else
0326         {
0327             uint32_t primask = DisableGlobalIRQ();
0328             handle->tcdUsedNum++;
0329             EnableGlobalIRQ(primask);
0330         }
0331 
0332         nextTcdIndex = (handle->tcdUser + 1U) % handle->tcdNum;
0333 
0334         if (mappedChannel == 1U)
0335         {
0336             EDMA_PrepareTransferConfig(&config, (void *)(uint32_t *)startAddr, FSL_FEATURE_PDM_FIFO_WIDTH, 0,
0337                                        (uint8_t *)(uint32_t)currentTransfer->data, FSL_FEATURE_PDM_FIFO_WIDTH,
0338                                        FSL_FEATURE_PDM_FIFO_WIDTH, handle->count * (uint32_t)FSL_FEATURE_PDM_FIFO_WIDTH,
0339                                        currentTransfer->dataSize);
0340         }
0341         else
0342         {
0343             EDMA_PrepareTransferConfig(&config, (void *)(uint32_t *)startAddr, FSL_FEATURE_PDM_FIFO_WIDTH,
0344                                        FSL_FEATURE_PDM_FIFO_OFFSET, (uint8_t *)(uint32_t)currentTransfer->data,
0345                                        FSL_FEATURE_PDM_FIFO_WIDTH, (int16_t)destOffset,
0346                                        mappedChannel * (uint32_t)FSL_FEATURE_PDM_FIFO_WIDTH, currentTransfer->dataSize);
0347         }
0348 
0349         EDMA_TcdSetTransferConfig((edma_tcd_t *)&handle->tcd[handle->tcdUser], &config,
0350                                   (edma_tcd_t *)&handle->tcd[nextTcdIndex]);
0351 
0352         if (mappedChannel > 1U)
0353         {
0354             EDMA_TcdSetMinorOffsetConfig((edma_tcd_t *)&handle->tcd[handle->tcdUser], &minorOffset);
0355 
0356             if (handle->interleaveType == kPDM_EDMAMultiChannelInterleavePerChannelBlock)
0357             {
0358                 EDMA_TcdSetModulo((edma_tcd_t *)&handle->tcd[handle->tcdUser], modulo, kEDMA_ModuloDisable);
0359             }
0360         }
0361 
0362         EDMA_TcdEnableInterrupts((edma_tcd_t *)&handle->tcd[handle->tcdUser], (uint32_t)kEDMA_MajorInterruptEnable);
0363 
0364         handle->tcdUser = nextTcdIndex;
0365 
0366         currentTransfer = currentTransfer->linkTransfer;
0367 
0368         if (currentTransfer == xfer)
0369         {
0370             handle->isLoopTransfer = true;
0371             break;
0372         }
0373     }
0374 
0375     if (handle->state != (uint32_t)kPDM_Busy)
0376     {
0377         EDMA_InstallTCD(handle->dmaHandle->base, handle->dmaHandle->channel, (edma_tcd_t *)&handle->tcd[tcdIndex]);
0378         /* Start DMA transfer */
0379         EDMA_StartTransfer(handle->dmaHandle);
0380 
0381         /* Enable DMA enable bit */
0382         PDM_EnableDMA(base, true);
0383         /* enable PDM */
0384         PDM_Enable(base, true);
0385 
0386         handle->state = (uint32_t)kPDM_Busy;
0387     }
0388 
0389     return kStatus_Success;
0390 }
0391 
0392 /*!
0393  * brief Aborts a PDM receive using eDMA.
0394  *
0395  * This function only aborts the current transfer slots, the other transfer slots' information still kept
0396  * in the handler. If users want to terminate all transfer slots, just call PDM_TransferTerminateReceiveEDMA.
0397  *
0398  * param base PDM base pointer
0399  * param handle PDM eDMA handle pointer.
0400  */
0401 void PDM_TransferAbortReceiveEDMA(PDM_Type *base, pdm_edma_handle_t *handle)
0402 {
0403     assert(handle != NULL);
0404 
0405     /* Disable dma */
0406     EDMA_AbortTransfer(handle->dmaHandle);
0407 
0408     /* Disable DMA enable bit */
0409     PDM_EnableDMA(base, false);
0410 
0411     /* Disable PDM */
0412     PDM_Enable(base, false);
0413 
0414     /* Handle the queue index */
0415     handle->tcdUsedNum--;
0416 
0417     /* Set the handle state */
0418     handle->state = (uint32_t)kPDM_Idle;
0419 }
0420 
0421 /*!
0422  * brief Terminate all PDM receive.
0423  *
0424  * This function will clear all transfer slots buffered in the pdm queue. If users only want to abort the
0425  * current transfer slot, please call PDM_TransferAbortReceiveEDMA.
0426  *
0427  * param base PDM base pointer.
0428  * param handle PDM eDMA handle pointer.
0429  */
0430 void PDM_TransferTerminateReceiveEDMA(PDM_Type *base, pdm_edma_handle_t *handle)
0431 {
0432     assert(handle != NULL);
0433 
0434     /* Abort the current transfer */
0435     PDM_TransferAbortReceiveEDMA(base, handle);
0436 
0437     /* Clear all the internal information */
0438     (void)memset(handle->tcd, 0, sizeof(edma_tcd_t) * handle->tcdNum);
0439     handle->tcdUser    = 0U;
0440     handle->tcdUsedNum = 0U;
0441 }
0442 
0443 /*!
0444  * brief Gets byte count received by PDM.
0445  *
0446  * param base PDM base pointer
0447  * param handle PDM eDMA handle pointer.
0448  * param count Bytes count received by PDM.
0449  * retval kStatus_Success Succeed get the transfer count.
0450  * retval kStatus_NoTransferInProgress There is no non-blocking transaction in progress.
0451  */
0452 status_t PDM_TransferGetReceiveCountEDMA(PDM_Type *base, pdm_edma_handle_t *handle, size_t *count)
0453 {
0454     assert(handle != NULL);
0455 
0456     *count = handle->receivedBytes;
0457 
0458     return kStatus_Success;
0459 }