blob: 703d5afab46170e5be88815d3a60d9d904fa7192 [file] [log] [blame]
/******************************************************************************
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;
}