2010-08-28 05:59:27 +08:00
|
|
|
/*
|
2012-03-22 02:56:05 +08:00
|
|
|
* Copyright (C) 2005-2012 Digium, Inc.
|
2010-08-28 05:59:27 +08:00
|
|
|
*
|
|
|
|
* Mark Spencer <markster@digium.com>
|
|
|
|
*
|
|
|
|
* All Rights Reserved
|
|
|
|
*/
|
|
|
|
|
|
|
|
/*
|
|
|
|
* See http://www.asterisk.org for more information about
|
|
|
|
* the Asterisk project. Please do not directly contact
|
|
|
|
* any of the maintainers of this project for assistance;
|
|
|
|
* the project provides a web site, mailing lists and IRC
|
|
|
|
* channels for your use.
|
|
|
|
*
|
|
|
|
* This program is free software, distributed under the terms of
|
|
|
|
* the GNU General Public License Version 2 as published by the
|
|
|
|
* Free Software Foundation. See the LICENSE file included with
|
|
|
|
* this program for more details.
|
|
|
|
*/
|
|
|
|
|
2013-05-10 00:57:52 +08:00
|
|
|
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
|
|
|
|
|
2010-08-28 05:59:27 +08:00
|
|
|
#include <linux/slab.h>
|
|
|
|
#include <linux/vmalloc.h>
|
|
|
|
#include <linux/string.h>
|
|
|
|
#include <linux/time.h>
|
|
|
|
#include <linux/version.h>
|
|
|
|
|
2013-05-10 00:57:52 +08:00
|
|
|
#include <dahdi/kernel.h>
|
|
|
|
#include <stdbool.h>
|
|
|
|
|
2010-08-28 05:59:27 +08:00
|
|
|
#include "vpm450m.h"
|
2013-06-25 04:22:18 +08:00
|
|
|
#include <oct612x.h>
|
2010-08-28 05:59:27 +08:00
|
|
|
|
2013-06-25 04:22:18 +08:00
|
|
|
static int wct4xxp_oct612x_write(struct oct612x_context *context,
|
|
|
|
u32 address, u16 value)
|
2010-08-28 05:59:27 +08:00
|
|
|
{
|
2013-06-25 04:22:18 +08:00
|
|
|
struct t4 *wc = dev_get_drvdata(context->dev);
|
|
|
|
oct_set_reg(wc, address, value);
|
|
|
|
return 0;
|
2010-08-28 05:59:27 +08:00
|
|
|
}
|
|
|
|
|
2013-06-25 04:22:18 +08:00
|
|
|
static int wct4xxp_oct612x_read(struct oct612x_context *context, u32 address,
|
|
|
|
u16 *value)
|
2010-08-28 05:59:27 +08:00
|
|
|
{
|
2013-06-25 04:22:18 +08:00
|
|
|
struct t4 *wc = dev_get_drvdata(context->dev);
|
|
|
|
*value = (u16)oct_get_reg(wc, address);
|
|
|
|
return 0;
|
2010-08-28 05:59:27 +08:00
|
|
|
}
|
|
|
|
|
2013-06-25 04:22:18 +08:00
|
|
|
static int wct4xxp_oct612x_write_smear(struct oct612x_context *context,
|
|
|
|
u32 address, u16 value, size_t count)
|
2010-08-28 05:59:27 +08:00
|
|
|
{
|
2013-06-25 04:22:18 +08:00
|
|
|
struct t4 *wc = dev_get_drvdata(context->dev);
|
|
|
|
int i;
|
|
|
|
for (i = 0; i < count; ++i)
|
|
|
|
oct_set_reg(wc, address + (i << 1), value);
|
|
|
|
return 0;
|
2010-08-28 05:59:27 +08:00
|
|
|
}
|
|
|
|
|
2013-06-25 04:22:18 +08:00
|
|
|
static int wct4xxp_oct612x_write_burst(struct oct612x_context *context,
|
|
|
|
u32 address, const u16 *buffer,
|
|
|
|
size_t count)
|
2010-08-28 05:59:27 +08:00
|
|
|
{
|
2013-06-25 04:22:18 +08:00
|
|
|
struct t4 *wc = dev_get_drvdata(context->dev);
|
|
|
|
int i;
|
|
|
|
for (i = 0; i < count; ++i)
|
|
|
|
oct_set_reg(wc, address + (i << 1), buffer[i]);
|
|
|
|
return 0;
|
2010-08-28 05:59:27 +08:00
|
|
|
}
|
|
|
|
|
2013-06-25 04:22:18 +08:00
|
|
|
static int wct4xxp_oct612x_read_burst(struct oct612x_context *context,
|
|
|
|
u32 address, u16 *buffer, size_t count)
|
2010-08-28 05:59:27 +08:00
|
|
|
{
|
2013-06-25 04:22:18 +08:00
|
|
|
struct t4 *wc = dev_get_drvdata(context->dev);
|
|
|
|
int i;
|
|
|
|
for (i = 0; i < count; ++i)
|
|
|
|
buffer[i] = oct_get_reg(wc, address + (i << 1));
|
|
|
|
return 0;
|
2010-08-28 05:59:27 +08:00
|
|
|
}
|
|
|
|
|
2013-06-25 04:22:18 +08:00
|
|
|
static const struct oct612x_ops wct4xxp_oct612x_ops = {
|
|
|
|
.write = wct4xxp_oct612x_write,
|
|
|
|
.read = wct4xxp_oct612x_read,
|
|
|
|
.write_smear = wct4xxp_oct612x_write_smear,
|
|
|
|
.write_burst = wct4xxp_oct612x_write_burst,
|
|
|
|
.read_burst = wct4xxp_oct612x_read_burst,
|
|
|
|
};
|
2010-08-28 05:59:27 +08:00
|
|
|
|
|
|
|
#define SOUT_G168_1100GB_ON 0x40000004
|
|
|
|
#define SOUT_DTMF_1 0x40000011
|
|
|
|
#define SOUT_DTMF_2 0x40000012
|
|
|
|
#define SOUT_DTMF_3 0x40000013
|
|
|
|
#define SOUT_DTMF_A 0x4000001A
|
|
|
|
#define SOUT_DTMF_4 0x40000014
|
|
|
|
#define SOUT_DTMF_5 0x40000015
|
|
|
|
#define SOUT_DTMF_6 0x40000016
|
|
|
|
#define SOUT_DTMF_B 0x4000001B
|
|
|
|
#define SOUT_DTMF_7 0x40000017
|
|
|
|
#define SOUT_DTMF_8 0x40000018
|
|
|
|
#define SOUT_DTMF_9 0x40000019
|
|
|
|
#define SOUT_DTMF_C 0x4000001C
|
|
|
|
#define SOUT_DTMF_STAR 0x4000001E
|
|
|
|
#define SOUT_DTMF_0 0x40000010
|
|
|
|
#define SOUT_DTMF_POUND 0x4000001F
|
|
|
|
#define SOUT_DTMF_D 0x4000001D
|
|
|
|
|
|
|
|
#define ROUT_G168_2100GB_ON 0x10000000
|
|
|
|
#define ROUT_G168_2100GB_WSPR 0x10000002
|
|
|
|
#define ROUT_SOUT_G168_2100HB_END 0x50000003
|
|
|
|
#define ROUT_G168_1100GB_ON 0x10000004
|
|
|
|
|
|
|
|
#define ROUT_DTMF_1 0x10000011
|
|
|
|
#define ROUT_DTMF_2 0x10000012
|
|
|
|
#define ROUT_DTMF_3 0x10000013
|
|
|
|
#define ROUT_DTMF_A 0x1000001A
|
|
|
|
#define ROUT_DTMF_4 0x10000014
|
|
|
|
#define ROUT_DTMF_5 0x10000015
|
|
|
|
#define ROUT_DTMF_6 0x10000016
|
|
|
|
#define ROUT_DTMF_B 0x1000001B
|
|
|
|
#define ROUT_DTMF_7 0x10000017
|
|
|
|
#define ROUT_DTMF_8 0x10000018
|
|
|
|
#define ROUT_DTMF_9 0x10000019
|
|
|
|
#define ROUT_DTMF_C 0x1000001C
|
|
|
|
#define ROUT_DTMF_STAR 0x1000001E
|
|
|
|
#define ROUT_DTMF_0 0x10000010
|
|
|
|
#define ROUT_DTMF_POUND 0x1000001F
|
|
|
|
#define ROUT_DTMF_D 0x1000001D
|
|
|
|
|
|
|
|
#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 vpm450m {
|
|
|
|
tPOCT6100_INSTANCE_API pApiInstance;
|
2013-06-25 04:22:18 +08:00
|
|
|
struct oct612x_context context;
|
2011-11-01 23:27:21 +08:00
|
|
|
UINT32 aulEchoChanHndl[256];
|
|
|
|
int chanflags[256];
|
|
|
|
int ecmode[256];
|
2010-08-28 05:59:27 +08:00
|
|
|
int numchans;
|
|
|
|
};
|
|
|
|
|
|
|
|
#define FLAG_DTMF (1 << 0)
|
|
|
|
#define FLAG_MUTE (1 << 1)
|
|
|
|
#define FLAG_ECHO (1 << 2)
|
2013-05-07 01:33:52 +08:00
|
|
|
#define FLAG_ALAW (1 << 3)
|
2010-08-28 05:59:27 +08:00
|
|
|
|
|
|
|
static unsigned int tones[] = {
|
|
|
|
SOUT_DTMF_1,
|
|
|
|
SOUT_DTMF_2,
|
|
|
|
SOUT_DTMF_3,
|
|
|
|
SOUT_DTMF_A,
|
|
|
|
SOUT_DTMF_4,
|
|
|
|
SOUT_DTMF_5,
|
|
|
|
SOUT_DTMF_6,
|
|
|
|
SOUT_DTMF_B,
|
|
|
|
SOUT_DTMF_7,
|
|
|
|
SOUT_DTMF_8,
|
|
|
|
SOUT_DTMF_9,
|
|
|
|
SOUT_DTMF_C,
|
|
|
|
SOUT_DTMF_STAR,
|
|
|
|
SOUT_DTMF_0,
|
|
|
|
SOUT_DTMF_POUND,
|
|
|
|
SOUT_DTMF_D,
|
|
|
|
SOUT_G168_1100GB_ON,
|
|
|
|
|
|
|
|
ROUT_DTMF_1,
|
|
|
|
ROUT_DTMF_2,
|
|
|
|
ROUT_DTMF_3,
|
|
|
|
ROUT_DTMF_A,
|
|
|
|
ROUT_DTMF_4,
|
|
|
|
ROUT_DTMF_5,
|
|
|
|
ROUT_DTMF_6,
|
|
|
|
ROUT_DTMF_B,
|
|
|
|
ROUT_DTMF_7,
|
|
|
|
ROUT_DTMF_8,
|
|
|
|
ROUT_DTMF_9,
|
|
|
|
ROUT_DTMF_C,
|
|
|
|
ROUT_DTMF_STAR,
|
|
|
|
ROUT_DTMF_0,
|
|
|
|
ROUT_DTMF_POUND,
|
|
|
|
ROUT_DTMF_D,
|
|
|
|
ROUT_G168_1100GB_ON,
|
|
|
|
};
|
|
|
|
|
2013-05-07 01:33:52 +08:00
|
|
|
void vpm450m_set_alaw_companding(struct vpm450m *vpm450m, int channel,
|
|
|
|
bool alaw)
|
|
|
|
{
|
|
|
|
tOCT6100_CHANNEL_MODIFY *modify;
|
|
|
|
UINT32 ulResult;
|
|
|
|
UINT32 law_to_use = (alaw) ? cOCT6100_PCM_A_LAW :
|
|
|
|
cOCT6100_PCM_U_LAW;
|
|
|
|
|
|
|
|
if (channel >= ARRAY_SIZE(vpm450m->chanflags)) {
|
|
|
|
pr_err("Channel out of bounds in %s\n", __func__);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
/* If we're already in this companding mode, no need to do anything. */
|
|
|
|
if (alaw == ((vpm450m->chanflags[channel] & FLAG_ALAW) > 0))
|
|
|
|
return;
|
|
|
|
|
|
|
|
modify = kzalloc(sizeof(tOCT6100_CHANNEL_MODIFY), GFP_ATOMIC);
|
|
|
|
if (!modify) {
|
|
|
|
pr_notice("Unable to allocate memory for setec!\n");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
Oct6100ChannelModifyDef(modify);
|
|
|
|
modify->ulChannelHndl = vpm450m->aulEchoChanHndl[channel];
|
|
|
|
modify->fTdmConfigModified = TRUE;
|
|
|
|
modify->TdmConfig.ulSinPcmLaw = law_to_use;
|
|
|
|
modify->TdmConfig.ulRinPcmLaw = law_to_use;
|
|
|
|
modify->TdmConfig.ulSoutPcmLaw = law_to_use;
|
|
|
|
modify->TdmConfig.ulRoutPcmLaw = law_to_use;
|
|
|
|
ulResult = Oct6100ChannelModify(vpm450m->pApiInstance, modify);
|
|
|
|
if (ulResult != GENERIC_OK) {
|
|
|
|
pr_notice("Failed to apply echo can changes on channel %d %d %08x!\n",
|
|
|
|
vpm450m->aulEchoChanHndl[channel], channel, ulResult);
|
|
|
|
} else {
|
2013-11-04 23:07:29 +08:00
|
|
|
if (debug) {
|
|
|
|
pr_info("Changed companding on channel %d to %s.\n",
|
|
|
|
channel, (alaw) ? "alaw" : "ulaw");
|
|
|
|
}
|
2013-05-07 01:33:52 +08:00
|
|
|
if (alaw)
|
|
|
|
vpm450m->chanflags[channel] |= FLAG_ALAW;
|
|
|
|
else
|
|
|
|
vpm450m->chanflags[channel] &= ~(FLAG_ALAW);
|
|
|
|
}
|
|
|
|
kfree(modify);
|
|
|
|
}
|
|
|
|
|
2010-08-28 05:59:27 +08:00
|
|
|
static void vpm450m_setecmode(struct vpm450m *vpm450m, int channel, int mode)
|
|
|
|
{
|
|
|
|
tOCT6100_CHANNEL_MODIFY *modify;
|
|
|
|
UINT32 ulResult;
|
|
|
|
|
|
|
|
if (vpm450m->ecmode[channel] == mode)
|
|
|
|
return;
|
2014-06-18 03:51:39 +08:00
|
|
|
modify = kzalloc(sizeof(*modify), GFP_ATOMIC);
|
2010-08-28 05:59:27 +08:00
|
|
|
if (!modify) {
|
|
|
|
printk(KERN_NOTICE "wct4xxp: Unable to allocate memory for setec!\n");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
Oct6100ChannelModifyDef(modify);
|
|
|
|
modify->ulEchoOperationMode = mode;
|
|
|
|
modify->ulChannelHndl = vpm450m->aulEchoChanHndl[channel];
|
|
|
|
ulResult = Oct6100ChannelModify(vpm450m->pApiInstance, modify);
|
|
|
|
if (ulResult != GENERIC_OK) {
|
2011-02-11 00:22:49 +08:00
|
|
|
printk(KERN_NOTICE "Failed to apply echo can changes on channel %d %08x!\n", channel, ulResult);
|
2010-08-28 05:59:27 +08:00
|
|
|
} else {
|
|
|
|
#ifdef OCTASIC_DEBUG
|
|
|
|
printk(KERN_DEBUG "Echo can on channel %d set to %d\n", channel, mode);
|
|
|
|
#endif
|
|
|
|
vpm450m->ecmode[channel] = mode;
|
|
|
|
}
|
|
|
|
kfree(modify);
|
|
|
|
}
|
|
|
|
|
|
|
|
void vpm450m_setdtmf(struct vpm450m *vpm450m, int channel, int detect, int mute)
|
|
|
|
{
|
|
|
|
tOCT6100_CHANNEL_MODIFY *modify;
|
|
|
|
UINT32 ulResult;
|
|
|
|
|
2013-05-07 01:33:52 +08:00
|
|
|
if (channel >= ARRAY_SIZE(vpm450m->chanflags)) {
|
|
|
|
pr_err("Channel out of bounds in %s\n", __func__);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2014-06-18 03:51:39 +08:00
|
|
|
modify = kzalloc(sizeof(*modify), GFP_KERNEL);
|
2010-08-28 05:59:27 +08:00
|
|
|
if (!modify) {
|
|
|
|
printk(KERN_NOTICE "wct4xxp: Unable to allocate memory for setdtmf!\n");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
Oct6100ChannelModifyDef(modify);
|
|
|
|
modify->ulChannelHndl = vpm450m->aulEchoChanHndl[channel];
|
|
|
|
if (mute) {
|
|
|
|
vpm450m->chanflags[channel] |= FLAG_MUTE;
|
|
|
|
modify->VqeConfig.fDtmfToneRemoval = TRUE;
|
|
|
|
} else {
|
|
|
|
vpm450m->chanflags[channel] &= ~FLAG_MUTE;
|
|
|
|
modify->VqeConfig.fDtmfToneRemoval = FALSE;
|
|
|
|
}
|
|
|
|
if (detect)
|
|
|
|
vpm450m->chanflags[channel] |= FLAG_DTMF;
|
|
|
|
else
|
|
|
|
vpm450m->chanflags[channel] &= ~FLAG_DTMF;
|
|
|
|
if (vpm450m->chanflags[channel] & (FLAG_DTMF|FLAG_MUTE)) {
|
|
|
|
if (!(vpm450m->chanflags[channel] & FLAG_ECHO)) {
|
|
|
|
vpm450m_setecmode(vpm450m, channel, cOCT6100_ECHO_OP_MODE_HT_RESET);
|
|
|
|
vpm450m_setecmode(vpm450m, channel, cOCT6100_ECHO_OP_MODE_HT_FREEZE);
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
if (!(vpm450m->chanflags[channel] & FLAG_ECHO))
|
|
|
|
vpm450m_setecmode(vpm450m, channel, cOCT6100_ECHO_OP_MODE_DIGITAL);
|
|
|
|
}
|
|
|
|
|
|
|
|
ulResult = Oct6100ChannelModify(vpm450m->pApiInstance, modify);
|
|
|
|
if (ulResult != GENERIC_OK) {
|
|
|
|
printk(KERN_NOTICE "Failed to apply dtmf mute changes on channel %d!\n", channel);
|
|
|
|
}
|
|
|
|
/* printk(KERN_DEBUG "VPM450m: Setting DTMF on channel %d: %s / %s\n", channel, (detect ? "DETECT" : "NO DETECT"), (mute ? "MUTE" : "NO MUTE")); */
|
|
|
|
kfree(modify);
|
|
|
|
}
|
|
|
|
|
|
|
|
void vpm450m_setec(struct vpm450m *vpm450m, int channel, int eclen)
|
|
|
|
{
|
2013-05-07 01:33:52 +08:00
|
|
|
if (channel >= ARRAY_SIZE(vpm450m->chanflags)) {
|
|
|
|
pr_err("Channel out of bounds in %s\n", __func__);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2010-08-28 05:59:27 +08:00
|
|
|
if (eclen) {
|
|
|
|
vpm450m->chanflags[channel] |= FLAG_ECHO;
|
|
|
|
vpm450m_setecmode(vpm450m, channel, cOCT6100_ECHO_OP_MODE_HT_RESET);
|
|
|
|
vpm450m_setecmode(vpm450m, channel, cOCT6100_ECHO_OP_MODE_NORMAL);
|
|
|
|
} else {
|
|
|
|
vpm450m->chanflags[channel] &= ~FLAG_ECHO;
|
|
|
|
if (vpm450m->chanflags[channel] & (FLAG_DTMF | FLAG_MUTE)) {
|
|
|
|
vpm450m_setecmode(vpm450m, channel, cOCT6100_ECHO_OP_MODE_HT_RESET);
|
|
|
|
vpm450m_setecmode(vpm450m, channel, cOCT6100_ECHO_OP_MODE_HT_FREEZE);
|
|
|
|
} else
|
|
|
|
vpm450m_setecmode(vpm450m, channel, cOCT6100_ECHO_OP_MODE_DIGITAL);
|
|
|
|
}
|
|
|
|
/* printk(KERN_DEBUG "VPM450m: Setting EC on channel %d to %d\n", channel, eclen); */
|
|
|
|
}
|
|
|
|
|
|
|
|
int vpm450m_checkirq(struct vpm450m *vpm450m)
|
|
|
|
{
|
|
|
|
tOCT6100_INTERRUPT_FLAGS InterruptFlags;
|
|
|
|
|
|
|
|
Oct6100InterruptServiceRoutineDef(&InterruptFlags);
|
|
|
|
Oct6100InterruptServiceRoutine(vpm450m->pApiInstance, &InterruptFlags);
|
|
|
|
|
|
|
|
return InterruptFlags.fToneEventsPending ? 1 : 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
int vpm450m_getdtmf(struct vpm450m *vpm450m, int *channel, int *tone, int *start)
|
|
|
|
{
|
|
|
|
tOCT6100_TONE_EVENT tonefound;
|
|
|
|
tOCT6100_EVENT_GET_TONE tonesearch;
|
|
|
|
|
|
|
|
Oct6100EventGetToneDef(&tonesearch);
|
|
|
|
tonesearch.pToneEvent = &tonefound;
|
|
|
|
tonesearch.ulMaxToneEvent = 1;
|
2011-06-03 04:00:36 +08:00
|
|
|
Oct6100EventGetTone(vpm450m->pApiInstance, &tonesearch);
|
2010-08-28 05:59:27 +08:00
|
|
|
if (tonesearch.ulNumValidToneEvent) {
|
|
|
|
if (channel)
|
|
|
|
*channel = tonefound.ulUserChanId;
|
|
|
|
if (tone) {
|
|
|
|
switch(tonefound.ulToneDetected) {
|
|
|
|
case SOUT_DTMF_1:
|
|
|
|
*tone = '1';
|
|
|
|
break;
|
|
|
|
case SOUT_DTMF_2:
|
|
|
|
*tone = '2';
|
|
|
|
break;
|
|
|
|
case SOUT_DTMF_3:
|
|
|
|
*tone = '3';
|
|
|
|
break;
|
|
|
|
case SOUT_DTMF_A:
|
|
|
|
*tone = 'A';
|
|
|
|
break;
|
|
|
|
case SOUT_DTMF_4:
|
|
|
|
*tone = '4';
|
|
|
|
break;
|
|
|
|
case SOUT_DTMF_5:
|
|
|
|
*tone = '5';
|
|
|
|
break;
|
|
|
|
case SOUT_DTMF_6:
|
|
|
|
*tone = '6';
|
|
|
|
break;
|
|
|
|
case SOUT_DTMF_B:
|
|
|
|
*tone = 'B';
|
|
|
|
break;
|
|
|
|
case SOUT_DTMF_7:
|
|
|
|
*tone = '7';
|
|
|
|
break;
|
|
|
|
case SOUT_DTMF_8:
|
|
|
|
*tone = '8';
|
|
|
|
break;
|
|
|
|
case SOUT_DTMF_9:
|
|
|
|
*tone = '9';
|
|
|
|
break;
|
|
|
|
case SOUT_DTMF_C:
|
|
|
|
*tone = 'C';
|
|
|
|
break;
|
|
|
|
case SOUT_DTMF_STAR:
|
|
|
|
*tone = '*';
|
|
|
|
break;
|
|
|
|
case SOUT_DTMF_0:
|
|
|
|
*tone = '0';
|
|
|
|
break;
|
|
|
|
case SOUT_DTMF_POUND:
|
|
|
|
*tone = '#';
|
|
|
|
break;
|
|
|
|
case SOUT_DTMF_D:
|
|
|
|
*tone = 'D';
|
|
|
|
break;
|
|
|
|
case SOUT_G168_1100GB_ON:
|
|
|
|
*tone = 'f';
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
#ifdef OCTASIC_DEBUG
|
|
|
|
printk(KERN_DEBUG "Unknown tone value %08x\n", tonefound.ulToneDetected);
|
|
|
|
#endif
|
|
|
|
*tone = 'u';
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (start)
|
|
|
|
*start = (tonefound.ulEventType == cOCT6100_TONE_PRESENT);
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2013-06-25 04:22:18 +08:00
|
|
|
unsigned int get_vpm450m_capacity(struct device *device)
|
2010-08-28 05:59:27 +08:00
|
|
|
{
|
2013-06-25 04:22:18 +08:00
|
|
|
struct oct612x_context context;
|
2010-08-28 05:59:27 +08:00
|
|
|
UINT32 ulResult;
|
|
|
|
|
|
|
|
tOCT6100_API_GET_CAPACITY_PINS CapacityPins;
|
|
|
|
|
2013-06-25 04:22:18 +08:00
|
|
|
context.dev = device;
|
|
|
|
context.ops = &wct4xxp_oct612x_ops;
|
|
|
|
|
2010-08-28 05:59:27 +08:00
|
|
|
Oct6100ApiGetCapacityPinsDef(&CapacityPins);
|
2013-06-25 04:22:18 +08:00
|
|
|
CapacityPins.pProcessContext = &context;
|
2010-08-28 05:59:27 +08:00
|
|
|
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(KERN_DEBUG "Failed to get chip capacity, code %08x!\n", ulResult);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
return CapacityPins.ulCapacityValue;
|
|
|
|
}
|
|
|
|
|
2013-06-25 04:22:18 +08:00
|
|
|
struct vpm450m *init_vpm450m(struct device *device, int *isalaw,
|
|
|
|
int numspans, const struct firmware *firmware)
|
2010-08-28 05:59:27 +08:00
|
|
|
{
|
|
|
|
tOCT6100_CHIP_OPEN *ChipOpen;
|
|
|
|
tOCT6100_GET_INSTANCE_SIZE InstanceSize;
|
|
|
|
tOCT6100_CHANNEL_OPEN *ChannelOpen;
|
|
|
|
UINT32 ulResult;
|
2012-01-11 04:45:35 +08:00
|
|
|
const unsigned int mask = (8 == numspans) ? 0x7 : 0x3;
|
2011-11-01 23:27:21 +08:00
|
|
|
unsigned int sout_stream, rout_stream;
|
2010-08-28 05:59:27 +08:00
|
|
|
struct vpm450m *vpm450m;
|
|
|
|
int x,y,law;
|
|
|
|
|
2014-06-18 03:51:39 +08:00
|
|
|
vpm450m = kzalloc(sizeof(*vpm450m), GFP_KERNEL);
|
|
|
|
if (!vpm450m)
|
2010-08-28 05:59:27 +08:00
|
|
|
return NULL;
|
|
|
|
|
2013-06-25 04:22:18 +08:00
|
|
|
vpm450m->context.dev = device;
|
|
|
|
vpm450m->context.ops = &wct4xxp_oct612x_ops;
|
2010-08-28 05:59:27 +08:00
|
|
|
|
2014-06-18 03:51:39 +08:00
|
|
|
ChipOpen = kzalloc(sizeof(*ChipOpen), GFP_KERNEL);
|
|
|
|
if (!ChipOpen) {
|
|
|
|
kfree(vpm450m);
|
2010-08-28 05:59:27 +08:00
|
|
|
kfree(vpm450m);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2014-06-18 03:51:39 +08:00
|
|
|
ChannelOpen = kzalloc(sizeof(*ChannelOpen), GFP_KERNEL);
|
|
|
|
if (!ChannelOpen) {
|
2010-08-28 05:59:27 +08:00
|
|
|
kfree(vpm450m);
|
|
|
|
kfree(ChipOpen);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2011-02-11 00:22:49 +08:00
|
|
|
for (x = 0; x < ARRAY_SIZE(vpm450m->ecmode); x++)
|
2010-08-28 05:59:27 +08:00
|
|
|
vpm450m->ecmode[x] = -1;
|
|
|
|
|
|
|
|
vpm450m->numchans = numspans * 32;
|
|
|
|
printk(KERN_INFO "VPM450: echo cancellation for %d channels\n", vpm450m->numchans);
|
|
|
|
|
|
|
|
Oct6100ChipOpenDef(ChipOpen);
|
|
|
|
|
|
|
|
/* Setup Chip Open Parameters */
|
|
|
|
ChipOpen->ulUpclkFreq = cOCT6100_UPCLK_FREQ_33_33_MHZ;
|
|
|
|
Oct6100GetInstanceSizeDef(&InstanceSize);
|
|
|
|
|
2013-06-25 04:22:18 +08:00
|
|
|
ChipOpen->pProcessContext = &vpm450m->context;
|
2010-08-28 05:59:27 +08:00
|
|
|
|
|
|
|
ChipOpen->pbyImageFile = firmware->data;
|
|
|
|
ChipOpen->ulImageSize = firmware->size;
|
|
|
|
ChipOpen->fEnableMemClkOut = TRUE;
|
|
|
|
ChipOpen->ulMemClkFreq = cOCT6100_MCLK_FREQ_133_MHZ;
|
|
|
|
ChipOpen->ulMaxChannels = vpm450m->numchans;
|
|
|
|
ChipOpen->ulMemoryType = cOCT6100_MEM_TYPE_DDR;
|
|
|
|
ChipOpen->ulMemoryChipSize = cOCT6100_MEMORY_CHIP_SIZE_32MB;
|
|
|
|
ChipOpen->ulNumMemoryChips = 1;
|
|
|
|
ChipOpen->aulTdmStreamFreqs[0] = cOCT6100_TDM_STREAM_FREQ_8MHZ;
|
2011-02-11 00:22:49 +08:00
|
|
|
ChipOpen->ulMaxFlexibleConfParticipants = 0;
|
|
|
|
ChipOpen->ulMaxConfBridges = 0;
|
|
|
|
ChipOpen->ulMaxRemoteDebugSessions = 0;
|
|
|
|
ChipOpen->fEnableChannelRecording = FALSE;
|
|
|
|
ChipOpen->ulSoftToneEventsBufSize = 64;
|
|
|
|
|
2011-11-01 23:27:21 +08:00
|
|
|
if (vpm450m->numchans <= 128) {
|
|
|
|
ChipOpen->ulMaxTdmStreams = 4;
|
|
|
|
ChipOpen->ulTdmSampling = cOCT6100_TDM_SAMPLE_AT_FALLING_EDGE;
|
|
|
|
} else {
|
|
|
|
ChipOpen->ulMaxTdmStreams = 32;
|
|
|
|
ChipOpen->fEnableFastH100Mode = TRUE;
|
|
|
|
ChipOpen->ulTdmSampling = cOCT6100_TDM_SAMPLE_AT_RISING_EDGE;
|
|
|
|
}
|
|
|
|
|
2010-08-28 05:59:27 +08:00
|
|
|
#if 0
|
|
|
|
ChipOpen->fEnableAcousticEcho = TRUE;
|
|
|
|
#endif
|
|
|
|
|
|
|
|
ulResult = Oct6100GetInstanceSize(ChipOpen, &InstanceSize);
|
|
|
|
if (ulResult != cOCT6100_ERR_OK) {
|
|
|
|
printk(KERN_NOTICE "Failed to get instance size, code %08x!\n", ulResult);
|
|
|
|
kfree(vpm450m);
|
|
|
|
kfree(ChipOpen);
|
|
|
|
kfree(ChannelOpen);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
vpm450m->pApiInstance = vmalloc(InstanceSize.ulApiInstanceSize);
|
|
|
|
if (!vpm450m->pApiInstance) {
|
|
|
|
printk(KERN_NOTICE "Out of memory (can't allocate %d bytes)!\n", InstanceSize.ulApiInstanceSize);
|
|
|
|
kfree(vpm450m);
|
|
|
|
kfree(ChipOpen);
|
|
|
|
kfree(ChannelOpen);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
ulResult = Oct6100ChipOpen(vpm450m->pApiInstance, ChipOpen);
|
|
|
|
if (ulResult != cOCT6100_ERR_OK) {
|
|
|
|
printk(KERN_NOTICE "Failed to open chip, code %08x!\n", ulResult);
|
2010-12-16 01:53:31 +08:00
|
|
|
vfree(vpm450m->pApiInstance);
|
2010-08-28 05:59:27 +08:00
|
|
|
kfree(vpm450m);
|
|
|
|
kfree(ChipOpen);
|
|
|
|
kfree(ChannelOpen);
|
|
|
|
return NULL;
|
|
|
|
}
|
2011-11-01 23:27:21 +08:00
|
|
|
|
|
|
|
sout_stream = (8 == numspans) ? 29 : 2;
|
|
|
|
rout_stream = (8 == numspans) ? 24 : 3;
|
|
|
|
|
|
|
|
for (x = 0; x < ((8 == numspans) ? 256 : 128); x++) {
|
2010-08-28 05:59:27 +08:00
|
|
|
/* execute this loop always on 4 span cards but
|
|
|
|
* on 2 span cards only execute for the channels related to our spans */
|
|
|
|
if (( numspans > 2) || ((x & 0x03) <2)) {
|
|
|
|
/* span timeslots are interleaved 12341234...
|
|
|
|
* therefore, the lower 2 bits tell us which span this
|
|
|
|
* timeslot/channel
|
|
|
|
*/
|
2013-05-07 01:33:52 +08:00
|
|
|
if (isalaw[x & mask]) {
|
2010-08-28 05:59:27 +08:00
|
|
|
law = cOCT6100_PCM_A_LAW;
|
2013-05-07 01:33:52 +08:00
|
|
|
vpm450m->chanflags[x] |= FLAG_ALAW;
|
|
|
|
} else {
|
2010-08-28 05:59:27 +08:00
|
|
|
law = cOCT6100_PCM_U_LAW;
|
2013-05-07 01:33:52 +08:00
|
|
|
vpm450m->chanflags[x] &= ~(FLAG_ALAW);
|
|
|
|
}
|
2010-08-28 05:59:27 +08:00
|
|
|
Oct6100ChannelOpenDef(ChannelOpen);
|
|
|
|
ChannelOpen->pulChannelHndl = &vpm450m->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;
|
2011-11-01 23:27:21 +08:00
|
|
|
ChannelOpen->TdmConfig.ulSoutStream = sout_stream;
|
2010-08-28 05:59:27 +08:00
|
|
|
ChannelOpen->TdmConfig.ulSoutTimeslot = x;
|
2011-11-01 23:27:21 +08:00
|
|
|
#if 1
|
2010-08-28 05:59:27 +08:00
|
|
|
ChannelOpen->TdmConfig.ulRoutPcmLaw = law;
|
2011-11-01 23:27:21 +08:00
|
|
|
ChannelOpen->TdmConfig.ulRoutStream = rout_stream;
|
2010-08-28 05:59:27 +08:00
|
|
|
ChannelOpen->TdmConfig.ulRoutTimeslot = x;
|
2011-11-01 23:27:21 +08:00
|
|
|
#endif
|
2010-08-28 05:59:27 +08:00
|
|
|
ChannelOpen->VqeConfig.fEnableNlp = TRUE;
|
|
|
|
ChannelOpen->VqeConfig.fRinDcOffsetRemoval = TRUE;
|
|
|
|
ChannelOpen->VqeConfig.fSinDcOffsetRemoval = TRUE;
|
|
|
|
|
|
|
|
ChannelOpen->fEnableToneDisabler = TRUE;
|
|
|
|
ChannelOpen->ulEchoOperationMode = cOCT6100_ECHO_OP_MODE_DIGITAL;
|
|
|
|
|
|
|
|
ulResult = Oct6100ChannelOpen(vpm450m->pApiInstance, ChannelOpen);
|
|
|
|
if (ulResult != GENERIC_OK) {
|
2011-11-01 23:27:21 +08:00
|
|
|
printk(KERN_NOTICE "Failed to open channel %d %x!\n", x, ulResult);
|
2011-02-11 00:22:49 +08:00
|
|
|
continue;
|
2010-08-28 05:59:27 +08:00
|
|
|
}
|
2011-02-11 00:22:49 +08:00
|
|
|
for (y = 0; y < ARRAY_SIZE(tones); y++) {
|
2010-08-28 05:59:27 +08:00
|
|
|
tOCT6100_TONE_DETECTION_ENABLE enable;
|
|
|
|
Oct6100ToneDetectionEnableDef(&enable);
|
|
|
|
enable.ulChannelHndl = vpm450m->aulEchoChanHndl[x];
|
|
|
|
enable.ulToneNumber = tones[y];
|
|
|
|
if (Oct6100ToneDetectionEnable(vpm450m->pApiInstance, &enable) != GENERIC_OK)
|
|
|
|
printk(KERN_NOTICE "Failed to enable tone detection on channel %d for tone %d!\n", x, y);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
kfree(ChipOpen);
|
|
|
|
kfree(ChannelOpen);
|
|
|
|
return vpm450m;
|
|
|
|
}
|
|
|
|
|
|
|
|
void release_vpm450m(struct vpm450m *vpm450m)
|
|
|
|
{
|
|
|
|
UINT32 ulResult;
|
|
|
|
tOCT6100_CHIP_CLOSE ChipClose;
|
|
|
|
|
|
|
|
Oct6100ChipCloseDef(&ChipClose);
|
|
|
|
ulResult = Oct6100ChipClose(vpm450m->pApiInstance, &ChipClose);
|
|
|
|
if (ulResult != cOCT6100_ERR_OK) {
|
|
|
|
printk(KERN_NOTICE "Failed to close chip, code %08x!\n", ulResult);
|
|
|
|
}
|
|
|
|
vfree(vpm450m->pApiInstance);
|
|
|
|
kfree(vpm450m);
|
|
|
|
}
|