| /****************************************************************************** |
| |
| Copyright (c) 2006-2015 Lantiq Deutschland GmbH |
| Copyright (c) 2015 Lantiq Beteiligungs-GmbH & Co.KG |
| Copyright 2018, Intel Corporation. |
| |
| For licensing information, see the file 'LICENSE' in the root folder of |
| this software module. |
| |
| ******************************************************************************/ |
| |
| /** |
| \file dxs_pcm.c |
| Implementation of PCM functions. |
| */ |
| |
| /* ========================================================================== */ |
| /* Includes */ |
| /* ========================================================================== */ |
| #include "dxs_config.h" |
| #include "dxs_lib.h" |
| #include "dxs_fw_cmd.h" |
| #include "dxs_errno.h" |
| #include "dxs_error.h" |
| |
| /* ========================================================================== */ |
| /* Macro definitions */ |
| /* ========================================================================== */ |
| #define DXS_FW_PCM_IF_BIT_OFF_MAX 7 |
| #define DXS_FW_PCM_CH_CODEC_LINEAR 0 |
| #define DXS_FW_PCM_CH_CODEC_G711A 2 |
| #define DXS_FW_PCM_CH_CODEC_G711U 3 |
| |
| /* ========================================================================== */ |
| /* Type definitions */ |
| /* ========================================================================== */ |
| /** Data structure for PCM interface configuration firmware command */ |
| typedef struct |
| { |
| #if __BYTE_ORDER == __BIG_ENDIAN |
| CMD_HEAD_BE; |
| /** Enable */ |
| uint32_t EN : 1; |
| /** Data Streaming Enable */ |
| uint32_t DS : 1; |
| /** Reserved */ |
| uint32_t Res02 : 3; |
| /** Transmit Bit Offset */ |
| uint32_t XOFF : 3; |
| /** Double Bit Clock */ |
| uint32_t DBL : 1; |
| /** Transmit Slope */ |
| uint32_t XS : 1; |
| /** Receive Slope */ |
| uint32_t RS : 1; |
| /** Bit 0 Drive Length */ |
| uint32_t DRV0 : 1; |
| /** Shift Control */ |
| uint32_t SH : 1; |
| /** Receive Bit Offset */ |
| uint32_t ROFF : 3; |
| /** Reserved */ |
| uint32_t Res03 : 16; |
| #else |
| CMD_HEAD_LE; |
| /** Reserved */ |
| uint32_t Res03 : 16; |
| /** Receive Bit Offset */ |
| uint32_t ROFF : 3; |
| /** Shift Control */ |
| uint32_t SH : 1; |
| /** Bit 0 Drive Length */ |
| uint32_t DRV0 : 1; |
| /** Receive Slope */ |
| uint32_t RS : 1; |
| /** Transmit Slope */ |
| uint32_t XS : 1; |
| /** Double Bit Clock */ |
| uint32_t DBL : 1; |
| /** Transmit Bit Offset */ |
| uint32_t XOFF : 3; |
| /** Reserved */ |
| uint32_t Res02 : 3; |
| /** Data Streaming Enable */ |
| uint32_t DS : 1; |
| /** Enable */ |
| uint32_t EN : 1; |
| #endif |
| } __attribute__ ((packed)) DXS_FW_PCM_IF_t; |
| #define DXS_FW_PCM_IF_ECMD 0 |
| #define DXS_FW_PCM_IF_LENGTH 4 |
| |
| /** Data structure for PCM channel activation firmware command */ |
| typedef struct |
| { |
| #if __BYTE_ORDER == __BIG_ENDIAN |
| CMD_HEAD_BE; |
| /** Enable */ |
| uint32_t EN : 1; |
| /** Coder */ |
| uint32_t COD : 3; |
| /** Wideband 16 kHz */ |
| uint32_t WIDE : 1; |
| /** Wide Band PCM Time Slot Configuration Bit */ |
| uint32_t WBTSC : 1; |
| /** Reserved */ |
| uint32_t Res02 : 11; |
| /** Transmit Highway Time Slot */ |
| uint32_t XTS : 7; |
| /** Reserved */ |
| uint32_t Res03 : 1; |
| /** Receive Highway Time Slot */ |
| uint32_t RTS : 7; |
| #else |
| CMD_HEAD_LE; |
| /** Receive Highway Time Slot */ |
| uint32_t RTS : 7; |
| /** Reserved */ |
| uint32_t Res03 : 1; |
| /** Transmit Highway Time Slot */ |
| uint32_t XTS : 7; |
| /** Reserved */ |
| uint32_t Res02 : 11; |
| /** Wide Band PCM Time Slot Configuration Bit */ |
| uint32_t WBTSC : 1; |
| /** Wideband 16 kHz */ |
| uint32_t WIDE : 1; |
| /** Coder */ |
| uint32_t COD : 3; |
| /** Enable */ |
| uint32_t EN : 1; |
| #endif |
| } __attribute__ ((packed)) DXS_FW_PCM_CH_t; |
| #define DXS_FW_PCM_CH_ECMD 1 |
| #define DXS_FW_PCM_CH_LENGTH 4 |
| |
| /** Data structure for PCM channel mute firmware command */ |
| typedef struct |
| { |
| #if __BYTE_ORDER == __BIG_ENDIAN |
| CMD_HEAD_BE; |
| /** Reserved */ |
| uint32_t Res02 : 31; |
| /** Mute of RX Direction */ |
| uint32_t RX_MUTE : 1; |
| #else |
| CMD_HEAD_LE; |
| /** Mute of RX Direction */ |
| uint32_t RX_MUTE : 1; |
| /** Reserved */ |
| uint32_t Res02 : 31; |
| #endif |
| } __attribute__ ((packed)) DXS_FW_PCM_CH_MUTE_t; |
| #define DXS_FW_PCM_CH_MUTE_ECMD 4 |
| #define DXS_FW_PCM_CH_MUTE_LENGTH 4 |
| |
| /** Data structure for PCM device resource */ |
| typedef struct |
| { |
| /** status flag */ |
| uint32_t flag; |
| #define PCM_DEV_INITIALIZED 1 |
| /** PCM Interface Control firmware message */ |
| DXS_FW_PCM_IF_t pcm_if; |
| } DXS_PCM_Dev_Resource_t; |
| |
| /** Data structure for PCM channel resource */ |
| typedef struct |
| { |
| /** status flag */ |
| uint32_t flag; |
| #define PCM_CH_INITIALIZED 1 |
| /** PCM Channel Control firmware message */ |
| DXS_FW_PCM_CH_t pcm_ch; |
| /** PCM Channel Mute firmware message */ |
| DXS_FW_PCM_CH_MUTE_t pcm_ch_mute; |
| } DXS_PCM_Ch_Resource_t; |
| |
| /* ========================================================================== */ |
| /* Global variables */ |
| /* ========================================================================== */ |
| static DXS_PCM_Dev_Resource_t pcm_devs[DXS_MAX_DEVICES] = {0}; |
| static DXS_PCM_Ch_Resource_t pcm_chan[DXS_MAX_DEVICES * CH_PER_DEVICE] = {0}; |
| |
| /* ========================================================================== */ |
| /* Function prototypes */ |
| /* ========================================================================== */ |
| |
| /* ========================================================================== */ |
| /* Function implementation */ |
| /* ========================================================================== */ |
| /** |
| Function dxs_pcm_dev_init |
| |
| \param devnum - device number |
| |
| */ |
| void *dxs_pcm_dev_init(uint8_t devnum) |
| { |
| DXS_PCM_Dev_Resource_t *p; |
| |
| if (devnum >= DXS_MAX_DEVICES) |
| return (void *)NULL; |
| |
| p = &pcm_devs[devnum]; |
| |
| p->flag = 0; |
| |
| p->pcm_if.CMD = CMD_EOP; |
| p->pcm_if.MOD = MOD_SDD_PCM; |
| p->pcm_if.ECMD = DXS_FW_PCM_IF_ECMD; |
| p->pcm_if.LENGTH = DXS_FW_PCM_IF_LENGTH; |
| /* DS bit should always be set to one (= double buffer) */ |
| p->pcm_if.DS = 1; |
| |
| p->flag |= PCM_DEV_INITIALIZED; |
| |
| return (void *)p; |
| } |
| |
| /** |
| Function dxs_pcm_ch_init |
| |
| \param dev - device number |
| \param ch - channel number |
| |
| */ |
| void *dxs_pcm_ch_init(uint8_t dev, uint8_t ch) |
| { |
| DXS_PCM_Ch_Resource_t *p; |
| |
| if (dev >= DXS_MAX_DEVICES || ch >= CH_PER_DEVICE) |
| return (void *)NULL; |
| |
| p = &pcm_chan[dev * CH_PER_DEVICE + ch]; |
| |
| p->flag = 0; |
| |
| p->pcm_ch.CMD = CMD_EOP; |
| p->pcm_ch.MOD = MOD_SDD_PCM; |
| p->pcm_ch.ECMD = DXS_FW_PCM_CH_ECMD; |
| p->pcm_ch.LENGTH = DXS_FW_PCM_CH_LENGTH; |
| p->pcm_ch.CHAN = ch; |
| |
| p->pcm_ch_mute.CMD = CMD_EOP; |
| p->pcm_ch_mute.MOD = MOD_SDD_PCM; |
| p->pcm_ch_mute.ECMD = DXS_FW_PCM_CH_MUTE_ECMD; |
| p->pcm_ch_mute.LENGTH = DXS_FW_PCM_CH_MUTE_LENGTH; |
| p->pcm_ch_mute.CHAN = ch; |
| |
| p->flag |= PCM_CH_INITIALIZED; |
| |
| return (void *)p; |
| } |
| |
| /** |
| Function dxs_pcm_if_config |
| |
| \param pDev - pointer to DXS device structure |
| \param xoff - transmit bit offset |
| \param dbl - double bit clock |
| \param xs - transmit slope |
| \param rs - receive slope |
| \param drv0 - bit 0 drive length |
| \param sh - shift control |
| \param roff - receive bit offset |
| |
| \return |
| - DXS_status_t |
| */ |
| int32_t dxs_pcm_if_config(DXS_DEVICE_t *pDev, uint8_t xoff, uint8_t dbl, |
| uint8_t xs, uint8_t rs, uint8_t drv0, uint8_t sh, |
| uint8_t roff) |
| { |
| DXS_PCM_Dev_Resource_t *p; |
| int32_t err; |
| |
| if (pDev == NULL) |
| { |
| DXS_RETURN(DXS_statusInvalidParam); |
| } |
| p = (DXS_PCM_Dev_Resource_t *)pDev->pcm; |
| |
| if (!(p->flag & PCM_DEV_INITIALIZED)) |
| { |
| DXS_RETURN(DXS_statusNotInitResource); |
| } |
| |
| /* parameter sanity check */ |
| if (xoff > DXS_FW_PCM_IF_BIT_OFF_MAX || roff > DXS_FW_PCM_IF_BIT_OFF_MAX || |
| dbl > 1 || xs > 1 || rs > 1 || drv0 > 1 || sh > 1) |
| { |
| DXS_RETURN(DXS_statusParamsOutRange); |
| } |
| |
| if (p->pcm_if.EN == 1) |
| { |
| uint8_t ch; |
| |
| /* check if there are active channels */ |
| for (ch = 0; ch < pDev->nChannels; ch++) |
| { |
| DXS_PCM_Ch_Resource_t *pPcmChRes = |
| (DXS_PCM_Ch_Resource_t *)pDev->pChannel[ch].pcm; |
| |
| if ((pPcmChRes->flag & PCM_CH_INITIALIZED) && pPcmChRes->pcm_ch.EN == 1) |
| { |
| /* error code - there are active PCM timeslots on this highway */ |
| DXS_RETURN(DXS_statusPcmChNotDisabled); |
| } |
| } |
| |
| p->pcm_if.EN = 0; |
| err = CmdWrite(pDev, (uint32_t *)&p->pcm_if); |
| if (err) |
| { |
| DXS_RETURN(DXS_statusPcmIfActError); |
| } |
| } |
| |
| p->pcm_if.XOFF = xoff; |
| p->pcm_if.DBL = dbl; |
| p->pcm_if.XS = xs; |
| p->pcm_if.RS = rs; |
| p->pcm_if.DRV0 = drv0; |
| p->pcm_if.SH = sh; |
| p->pcm_if.ROFF = roff; |
| p->pcm_if.EN = 1; |
| |
| err = CmdWrite(pDev, (uint32_t *)&p->pcm_if); |
| if (err) |
| { |
| DXS_RETURN(DXS_statusPcmIfActError); |
| } |
| |
| return DXS_statusOk; |
| } |
| |
| /** |
| Function dxs_pcm_ch_act |
| |
| \param pCh - pointer to DXS channel structure |
| \param line - line number |
| \param wb - wideband |
| \param wbtsc - wideband PCM time slot configuration |
| \param codec - codec |
| \param xts - transmit highway time slot |
| \param rts - receive highway time slot |
| |
| \return |
| - DXS_status_t |
| */ |
| int32_t dxs_pcm_ch_act(DXS_CHANNEL_t *pCh, uint8_t wb, uint8_t wbtsc, |
| uint8_t codec, uint8_t xts, uint8_t rts) |
| { |
| DXS_PCM_Ch_Resource_t *p; |
| DXS_PCM_Dev_Resource_t *pPcmDevRes; |
| int32_t err; |
| |
| if (pCh == NULL) |
| { |
| DXS_RETURN(DXS_statusInvalidParam); |
| } |
| p = (DXS_PCM_Ch_Resource_t *)pCh->pcm; |
| |
| if (!(p->flag & PCM_CH_INITIALIZED)) |
| { |
| DXS_RETURN(DXS_statusNotInitResource); |
| } |
| |
| pPcmDevRes = (DXS_PCM_Dev_Resource_t *)pCh->pParent->pcm; |
| if (pPcmDevRes->pcm_if.EN != 1) |
| { |
| /* error code - PCM interface is not enabled */ |
| DXS_RETURN(DXS_statusPcmIfNotEnabled); |
| } |
| |
| /* parameter sanity check */ |
| if (wb > 1 || wbtsc > 1 || |
| (codec != DXS_FW_PCM_CH_CODEC_LINEAR && |
| codec != DXS_FW_PCM_CH_CODEC_G711A && |
| codec != DXS_FW_PCM_CH_CODEC_G711U)) |
| { |
| DXS_RETURN(DXS_statusParamsOutRange); |
| } |
| |
| if (p->pcm_ch.EN == 1) |
| { |
| if (p->pcm_ch.COD != codec || p->pcm_ch.WIDE != wb || |
| p->pcm_ch.WBTSC != wbtsc || p->pcm_ch.XTS != xts || p->pcm_ch.RTS != rts) |
| { |
| p->pcm_ch.EN = 0; |
| err = CmdWrite(pCh->pParent, (uint32_t *)&p->pcm_ch); |
| if (err) |
| { |
| DXS_RETURN(DXS_statusPcmChActError); |
| } |
| } |
| else |
| { |
| /* parameters aren't changed */ |
| return DXS_statusOk; |
| } |
| } |
| |
| p->pcm_ch.COD = codec; |
| p->pcm_ch.WIDE = wb; |
| p->pcm_ch.WBTSC = wbtsc; |
| p->pcm_ch.XTS = xts; |
| p->pcm_ch.RTS = rts; |
| /* always set EN bit */ |
| p->pcm_ch.EN = 1; |
| |
| err = CmdWrite(pCh->pParent, (uint32_t *)&p->pcm_ch); |
| if (err) |
| { |
| DXS_RETURN(DXS_statusPcmChActError); |
| } |
| |
| /* unmute Rx direction implicitly */ |
| if (p->pcm_ch_mute.RX_MUTE) |
| { |
| p->pcm_ch_mute.RX_MUTE = 0; |
| |
| err = CmdWrite(pCh->pParent, (uint32_t *)&p->pcm_ch_mute); |
| if (err) |
| { |
| DXS_RETURN(DXS_statusPcmChActError); |
| } |
| } |
| |
| return DXS_statusOk; |
| } |
| |
| /** |
| Function dxs_pcm_ch_deact |
| |
| \param pCh - pointer to DXS channel structure |
| |
| \return |
| - DXS_status_t |
| */ |
| int32_t dxs_pcm_ch_deact(DXS_CHANNEL_t *pCh) |
| { |
| DXS_PCM_Ch_Resource_t *p; |
| int32_t err; |
| |
| if (pCh == NULL) |
| { |
| DXS_RETURN(DXS_statusInvalidParam); |
| } |
| p = (DXS_PCM_Ch_Resource_t *)pCh->pcm; |
| |
| if (!(p->flag & PCM_CH_INITIALIZED)) |
| { |
| DXS_RETURN(DXS_statusNotInitResource); |
| } |
| |
| if (p->pcm_ch.EN == 0) |
| { |
| /* already deactivated */ |
| return DXS_statusOk; |
| } |
| |
| p->pcm_ch.EN = 0; |
| |
| err = CmdWrite(pCh->pParent, (uint32_t *)&p->pcm_ch); |
| if (err) |
| { |
| DXS_RETURN(DXS_statusPcmChActError); |
| } |
| |
| return DXS_statusOk; |
| } |
| |
| /** |
| Function dxs_pcm_ch_mute |
| |
| \param pCh - pointer to DXS channel structure |
| \param action - action mute/unmute |
| |
| \return |
| - DXS_status_t |
| */ |
| int32_t dxs_pcm_ch_mute(DXS_CHANNEL_t *pCh, uint8_t action) |
| { |
| DXS_PCM_Ch_Resource_t *p; |
| int32_t err; |
| |
| if (pCh == NULL || action > 1) |
| { |
| DXS_RETURN(DXS_statusInvalidParam); |
| } |
| p = (DXS_PCM_Ch_Resource_t *)pCh->pcm; |
| |
| if (!(p->flag & PCM_CH_INITIALIZED)) |
| { |
| DXS_RETURN(DXS_statusNotInitResource); |
| } |
| |
| if (p->pcm_ch_mute.RX_MUTE == action) |
| { |
| /* parameters aren't changed */ |
| return DXS_statusOk; |
| } |
| |
| p->pcm_ch_mute.RX_MUTE = action; |
| |
| err = CmdWrite(pCh->pParent, (uint32_t *)&p->pcm_ch_mute); |
| if (err) |
| { |
| DXS_RETURN(DXS_statusPcmChMuteError); |
| } |
| |
| return DXS_statusOk; |
| } |