From 7a39bf8983515adaeac5b168103d9298dd448632 Mon Sep 17 00:00:00 2001 From: Tzafrir Cohen Date: Tue, 24 Aug 2010 12:22:29 +0300 Subject: initial ap400 driver --- drivers/dahdi/ap400/apec.c | 390 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 390 insertions(+) create mode 100644 drivers/dahdi/ap400/apec.c (limited to 'drivers/dahdi/ap400/apec.c') diff --git a/drivers/dahdi/ap400/apec.c b/drivers/dahdi/ap400/apec.c new file mode 100644 index 0000000..b43655e --- /dev/null +++ b/drivers/dahdi/ap400/apec.c @@ -0,0 +1,390 @@ +/* + * AP400 Echo Cancelation Hardware support + * + * Written by Wagner Gegler + * + * Based on previous work written by Mark Spencer + * + * Copyright (C) 2005-2006 Digium, Inc. + * + * Mark Spencer + * + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#include +#include +#include +#include +#include +#include + +#include "apec.h" +#include "oct6100api/oct6100_api.h" + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,18) +#include +#else +#include +#endif + +/* API for Octasic access */ +UINT32 Oct6100UserGetTime(tPOCT6100_GET_TIME f_pTime) +{ + /* Why couldn't they just take a timeval like everyone else? */ + struct timeval tv; + unsigned long long total_usecs; + unsigned int mask = ~0; + + do_gettimeofday(&tv); + total_usecs = (((unsigned long long)(tv.tv_sec)) * 1000000) + + (((unsigned long long)(tv.tv_usec))); + f_pTime->aulWallTimeUs[0] = (total_usecs & mask); + f_pTime->aulWallTimeUs[1] = (total_usecs >> 32); + return cOCT6100_ERR_OK; +} + +UINT32 Oct6100UserMemSet(PVOID f_pAddress, UINT32 f_ulPattern, UINT32 f_ulLength) +{ + memset(f_pAddress, f_ulPattern, f_ulLength); + return cOCT6100_ERR_OK; +} + +UINT32 Oct6100UserMemCopy(PVOID f_pDestination, const void *f_pSource, UINT32 f_ulLength) +{ + memcpy(f_pDestination, f_pSource, f_ulLength); + return cOCT6100_ERR_OK; +} + +UINT32 Oct6100UserCreateSerializeObject(tPOCT6100_CREATE_SERIALIZE_OBJECT f_pCreate) +{ + return cOCT6100_ERR_OK; +} + +UINT32 Oct6100UserDestroySerializeObject(tPOCT6100_DESTROY_SERIALIZE_OBJECT f_pDestroy) +{ +#ifdef OCTASIC_DEBUG + printk("I should never be called! (destroy serialize object)\n"); +#endif + return cOCT6100_ERR_OK; +} + +UINT32 Oct6100UserSeizeSerializeObject(tPOCT6100_SEIZE_SERIALIZE_OBJECT f_pSeize) +{ + /* Not needed */ + return cOCT6100_ERR_OK; +} + +UINT32 Oct6100UserReleaseSerializeObject(tPOCT6100_RELEASE_SERIALIZE_OBJECT f_pRelease) +{ + /* Not needed */ + return cOCT6100_ERR_OK; +} + +UINT32 Oct6100UserDriverWriteApi(tPOCT6100_WRITE_PARAMS f_pWriteParams) +{ + oct_write(f_pWriteParams->pProcessContext, f_pWriteParams->ulWriteAddress, f_pWriteParams->usWriteData); + return cOCT6100_ERR_OK; +} + +UINT32 Oct6100UserDriverWriteSmearApi(tPOCT6100_WRITE_SMEAR_PARAMS f_pSmearParams) +{ + unsigned int x; + for (x=0;xulWriteLength;x++) { + oct_write(f_pSmearParams->pProcessContext, f_pSmearParams->ulWriteAddress + (x << 1), f_pSmearParams->usWriteData); + } + return cOCT6100_ERR_OK; +} + +UINT32 Oct6100UserDriverWriteBurstApi(tPOCT6100_WRITE_BURST_PARAMS f_pBurstParams) +{ + unsigned int x; + for (x=0;xulWriteLength;x++) { + oct_write(f_pBurstParams->pProcessContext, f_pBurstParams->ulWriteAddress + (x << 1), f_pBurstParams->pusWriteData[x]); + } + return cOCT6100_ERR_OK; +} + +UINT32 Oct6100UserDriverReadApi(tPOCT6100_READ_PARAMS f_pReadParams) +{ + *(f_pReadParams->pusReadData) = oct_read(f_pReadParams->pProcessContext, f_pReadParams->ulReadAddress); + return cOCT6100_ERR_OK; +} + +UINT32 Oct6100UserDriverReadBurstApi(tPOCT6100_READ_BURST_PARAMS f_pBurstParams) +{ + unsigned int x; + for (x=0;xulReadLength;x++) { + f_pBurstParams->pusReadData[x] = oct_read(f_pBurstParams->pProcessContext, f_pBurstParams->ulReadAddress + (x << 1)); + } + return cOCT6100_ERR_OK; +} + +#if 0 +#define cOCT6100_ECHO_OP_MODE_DIGITAL cOCT6100_ECHO_OP_MODE_HT_FREEZE +#else +#define cOCT6100_ECHO_OP_MODE_DIGITAL cOCT6100_ECHO_OP_MODE_POWER_DOWN +#endif + +struct apec_s { + tPOCT6100_INSTANCE_API pApiInstance; + UINT32 aulEchoChanHndl[128]; + int chanflags[128]; + int ecmode[128]; + int numchans; +}; + +#define FLAG_DTMF (1 << 0) +#define FLAG_MUTE (1 << 1) +#define FLAG_ECHO (1 << 2) + +static void apec_setecmode(struct apec_s *apec, int channel, int mode) +{ + tOCT6100_CHANNEL_MODIFY *modify; + UINT32 ulResult; + + if (apec->ecmode[channel] == mode) + return; + modify = kmalloc(sizeof(tOCT6100_CHANNEL_MODIFY), GFP_ATOMIC); + if (!modify) { + printk("APEC: Unable to allocate memory for setec!\n"); + return; + } + Oct6100ChannelModifyDef(modify); + modify->ulEchoOperationMode = mode; + modify->ulChannelHndl = apec->aulEchoChanHndl[channel]; + ulResult = Oct6100ChannelModify(apec->pApiInstance, modify); + if (ulResult != GENERIC_OK) { + printk("Failed to apply echo can changes on channel %d!\n", channel); + } else { +#ifdef OCTASIC_DEBUG + printk("Echo can on channel %d set to %d\n", channel, mode); +#endif + apec->ecmode[channel] = mode; + } + kfree(modify); +} + +void apec_setec(struct apec_s *apec, int channel, int eclen) +{ + if (eclen) { + apec->chanflags[channel] |= FLAG_ECHO; + apec_setecmode(apec, channel, cOCT6100_ECHO_OP_MODE_HT_RESET); + apec_setecmode(apec, channel, cOCT6100_ECHO_OP_MODE_NORMAL); + } else { + apec->chanflags[channel] &= ~FLAG_ECHO; + if (apec->chanflags[channel] & (FLAG_DTMF | FLAG_MUTE)) { + apec_setecmode(apec, channel, cOCT6100_ECHO_OP_MODE_HT_RESET); + apec_setecmode(apec, channel, cOCT6100_ECHO_OP_MODE_HT_FREEZE); + } else + apec_setecmode(apec, channel, cOCT6100_ECHO_OP_MODE_DIGITAL); + } + printk("APEC: Setting EC on channel %d to %d\n", channel, eclen); +} + +int apec_checkirq(struct apec_s *apec) +{ + tOCT6100_INTERRUPT_FLAGS InterruptFlags; + + Oct6100InterruptServiceRoutineDef(&InterruptFlags); + Oct6100InterruptServiceRoutine(apec->pApiInstance, &InterruptFlags); + + return InterruptFlags.fToneEventsPending ? 1 : 0; +} + +unsigned int apec_capacity_get(void *wc) +{ + UINT32 ulResult; + + tOCT6100_API_GET_CAPACITY_PINS CapacityPins; + + Oct6100ApiGetCapacityPinsDef(&CapacityPins); + CapacityPins.pProcessContext = wc; + CapacityPins.ulMemoryType = cOCT6100_MEM_TYPE_DDR; + CapacityPins.fEnableMemClkOut = TRUE; + CapacityPins.ulMemClkFreq = cOCT6100_MCLK_FREQ_133_MHZ; + + ulResult = Oct6100ApiGetCapacityPins(&CapacityPins); + if (ulResult != cOCT6100_ERR_OK) { + printk("Failed to get chip capacity, code %08x!\n", ulResult); + return 0; + } + return CapacityPins.ulCapacityValue; +} + +struct apec_s *apec_init(void *wc, int *isalaw, int numspans, const struct firmware *firmware) +{ + tOCT6100_CHIP_OPEN *ChipOpen; + tOCT6100_GET_INSTANCE_SIZE InstanceSize; + tOCT6100_CHANNEL_OPEN *ChannelOpen; + UINT32 ulResult; + struct apec_s *apec; + int x, law; +#ifdef CONFIG_4KSTACKS + unsigned long flags; +#endif + + if (!(apec = kmalloc(sizeof(struct apec_s), GFP_KERNEL))) + return NULL; + + memset(apec, 0, sizeof(struct apec_s)); + + if (!(ChipOpen = kmalloc(sizeof(tOCT6100_CHIP_OPEN), GFP_KERNEL))) { + kfree(apec); + return NULL; + } + + memset(ChipOpen, 0, sizeof(tOCT6100_CHIP_OPEN)); + + if (!(ChannelOpen = kmalloc(sizeof(tOCT6100_CHANNEL_OPEN), GFP_KERNEL))) { + kfree(apec); + kfree(ChipOpen); + return NULL; + } + + memset(ChannelOpen, 0, sizeof(tOCT6100_CHANNEL_OPEN)); + + for (x=0;x<128;x++) + apec->ecmode[x] = -1; + + apec->numchans = numspans * 32; + printk("APEC: echo cancellation for %d channels\n", apec->numchans); + + Oct6100ChipOpenDef(ChipOpen); + + /* Setup Chip Open Parameters */ + ChipOpen->ulUpclkFreq = cOCT6100_UPCLK_FREQ_33_33_MHZ; + Oct6100GetInstanceSizeDef(&InstanceSize); + + ChipOpen->pProcessContext = wc; + + ChipOpen->pbyImageFile = firmware->data; + ChipOpen->ulImageSize = firmware->size; + + ChipOpen->fEnableMemClkOut = TRUE; + ChipOpen->ulMemClkFreq = cOCT6100_MCLK_FREQ_133_MHZ; + ChipOpen->ulMaxChannels = apec->numchans; + ChipOpen->ulMemoryType = cOCT6100_MEM_TYPE_DDR; + ChipOpen->ulMemoryChipSize = cOCT6100_MEMORY_CHIP_SIZE_32MB; + ChipOpen->ulNumMemoryChips = 1; + ChipOpen->ulMaxTdmStreams = 4; + ChipOpen->aulTdmStreamFreqs[0] = cOCT6100_TDM_STREAM_FREQ_8MHZ; + ChipOpen->ulTdmSampling = cOCT6100_TDM_SAMPLE_AT_FALLING_EDGE; +#if 0 + ChipOpen->fEnableAcousticEcho = TRUE; +#endif + + ulResult = Oct6100GetInstanceSize(ChipOpen, &InstanceSize); + if (ulResult != cOCT6100_ERR_OK) { + printk("Failed to get instance size, code %08x!\n", ulResult); + kfree(apec); + return NULL; + } + + + apec->pApiInstance = vmalloc(InstanceSize.ulApiInstanceSize); + if (!apec->pApiInstance) { + printk("Out of memory (can't allocate %d bytes)!\n", InstanceSize.ulApiInstanceSize); + kfree(apec); + kfree(ChipOpen); + kfree(ChannelOpen); + return NULL; + } + + /* I don't know what to curse more in this comment, the problems caused by + * the 4K kernel stack limit change or the octasic API for being so darn + * stack unfriendly. Stupid, stupid, stupid. So we disable IRQs so we + * don't run the risk of overflowing the stack while we initialize the + * octasic. */ +#ifdef CONFIG_4KSTACKS + local_irq_save(flags); +#endif + ulResult = Oct6100ChipOpen(apec->pApiInstance, ChipOpen); + if (ulResult != cOCT6100_ERR_OK) { + printk("Failed to open chip, code %08x!\n", ulResult); +#ifdef CONFIG_4KSTACKS + local_irq_restore(flags); +#endif + kfree(apec); + kfree(ChipOpen); + kfree(ChannelOpen); + return NULL; + } + for (x=0; x < 128; x++) { + /* execute this loop always on 4 span cards but + * on 2 span cards only execute for the channels related to our spans */ + if ((x & 0x3) < numspans) { + /* span timeslots are interleaved 12341234... + * therefore, the lower 2 bits tell us which span this + * timeslot/channel + */ + if (isalaw[x & 0x03]) + law = cOCT6100_PCM_A_LAW; + else + law = cOCT6100_PCM_U_LAW; + Oct6100ChannelOpenDef(ChannelOpen); + ChannelOpen->pulChannelHndl = &apec->aulEchoChanHndl[x]; + ChannelOpen->ulUserChanId = x; + ChannelOpen->TdmConfig.ulRinPcmLaw = law; + ChannelOpen->TdmConfig.ulRinStream = 0; + ChannelOpen->TdmConfig.ulRinTimeslot = x; + ChannelOpen->TdmConfig.ulSinPcmLaw = law; + ChannelOpen->TdmConfig.ulSinStream = 1; + ChannelOpen->TdmConfig.ulSinTimeslot = x; + ChannelOpen->TdmConfig.ulSoutPcmLaw = law; + ChannelOpen->TdmConfig.ulSoutStream = 2; + ChannelOpen->TdmConfig.ulSoutTimeslot = x; + ChannelOpen->TdmConfig.ulRoutPcmLaw = law; + ChannelOpen->TdmConfig.ulRoutStream = 3; + ChannelOpen->TdmConfig.ulRoutTimeslot = x; + ChannelOpen->VqeConfig.fEnableNlp = TRUE; + ChannelOpen->VqeConfig.fRinDcOffsetRemoval = TRUE; + ChannelOpen->VqeConfig.fSinDcOffsetRemoval = TRUE; + + ChannelOpen->fEnableToneDisabler = TRUE; + ChannelOpen->ulEchoOperationMode = cOCT6100_ECHO_OP_MODE_DIGITAL; + + ulResult = Oct6100ChannelOpen(apec->pApiInstance, ChannelOpen); + if (ulResult != GENERIC_OK) { + printk("Failed to open channel %d!\n", x); + } + } + } + +#ifdef CONFIG_4KSTACKS + local_irq_restore(flags); +#endif + kfree(ChipOpen); + kfree(ChannelOpen); + return apec; +} + +void apec_release(struct apec_s *apec) +{ + UINT32 ulResult; + tOCT6100_CHIP_CLOSE ChipClose; + + Oct6100ChipCloseDef(&ChipClose); + ulResult = Oct6100ChipClose(apec->pApiInstance, &ChipClose); + if (ulResult != cOCT6100_ERR_OK) { + printk("Failed to close chip, code %08x!\n", ulResult); + } + vfree(apec->pApiInstance); + kfree(apec); + printk(KERN_INFO "APEC: Releasing...\n"); +} -- cgit v1.2.3