dahdi-linux/drivers/dahdi/voicebus/GpakCust.c

1009 lines
25 KiB
C
Raw Normal View History

/*
* Copyright (c) 2005, Adaptive Digital Technologies, Inc.
* Copyright (c) 2005-2010, Digium Incorporated
*
* File Name: GpakCust.c
*
* Description:
* This file contains host system dependent functions to support generic
* G.PAK API functions. The file is integrated into the host processor
* connected to C55x G.PAK DSPs via a Host Port Interface.
*
* Note: This file is supplied by Adaptive Digital Technologies and
* modified by Digium in order to support the VPMADT032 modules.
*
* This program has been released under the terms of the GPL version 2 by
* permission of Adaptive Digital Technologies, Inc.
*
*/
/*
* 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.
*/
#include <linux/version.h>
#include <linux/types.h>
#include <linux/pci.h>
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,26)
#include <linux/semaphore.h>
#else
#include <asm/semaphore.h>
#endif
#include <dahdi/kernel.h>
#include <dahdi/user.h>
#include "GpakCust.h"
#include "GpakApi.h"
#include "adt_lec.h"
#include "voicebus.h"
#include "vpmadtreg.h"
static rwlock_t ifacelock;
static struct vpmadt032 *ifaces[MAX_DSP_CORES];
#define vpm_info(vpm, format, arg...) \
dev_info(&vpm->vb->pdev->dev , format , ## arg)
static inline struct vpmadt032 *find_iface(const unsigned short dspid)
{
struct vpmadt032 *ret;
read_lock(&ifacelock);
if (ifaces[dspid]) {
ret = ifaces[dspid];
} else {
ret = NULL;
}
read_unlock(&ifacelock);
return ret;
}
static struct vpmadt032_cmd *vpmadt032_get_free_cmd(struct vpmadt032 *vpm)
{
struct vpmadt032_cmd *cmd;
might_sleep();
cmd = kmalloc(sizeof(struct vpmadt032_cmd), GFP_KERNEL);
if (unlikely(!cmd))
return NULL;
memset(cmd, 0, sizeof(*cmd));
init_completion(&cmd->complete);
return cmd;
}
/* Wait for any outstanding commands to the VPMADT032 to complete */
static inline int vpmadt032_io_wait(struct vpmadt032 *vpm)
{
unsigned long flags;
int empty;
while (1) {
spin_lock_irqsave(&vpm->list_lock, flags);
empty = list_empty(&vpm->pending_cmds) && list_empty(&vpm->active_cmds);
spin_unlock_irqrestore(&vpm->list_lock, flags);
if (empty) {
break;
} else {
msleep(1);
}
}
return 0;
}
/* Issue a read command to a register on the VPMADT032. We'll get the results
* later. */
static struct vpmadt032_cmd *vpmadt032_getreg_full_async(struct vpmadt032 *vpm, int pagechange,
unsigned short addr)
{
unsigned long flags;
struct vpmadt032_cmd *cmd;
cmd = vpmadt032_get_free_cmd(vpm);
if (!cmd)
return NULL;
cmd->desc = (pagechange) ? __VPM150M_RWPAGE | __VPM150M_RD : __VPM150M_RD;
cmd->address = addr;
cmd->data = 0;
spin_lock_irqsave(&vpm->list_lock, flags);
list_add_tail(&cmd->node, &vpm->pending_cmds);
spin_unlock_irqrestore(&vpm->list_lock, flags);
return cmd;
}
/* Get the results from a previous call to vpmadt032_getreg_full_async. */
static int vpmadt032_getreg_full_return(struct vpmadt032 *vpm, int pagechange,
u16 addr, u16 *outbuf, struct vpmadt032_cmd *cmd)
{
unsigned long flags;
unsigned long ret;
BUG_ON(!cmd);
/* We'll wait for 200ms */
ret = wait_for_completion_timeout(&cmd->complete, HZ/5);
if (unlikely(!ret)) {
spin_lock_irqsave(&vpm->list_lock, flags);
list_del(&cmd->node);
spin_unlock_irqrestore(&vpm->list_lock, flags);
kfree(cmd);
return -EIO;
}
if (cmd->desc & __VPM150M_FIN) {
*outbuf = cmd->data;
cmd->desc = 0;
ret = 0;
}
list_del(&cmd->node);
kfree(cmd);
return 0;
}
/* Read one of the registers on the VPMADT032 */
static int vpmadt032_getreg_full(struct vpmadt032 *vpm, int pagechange, u16 addr, u16 *outbuf)
{
struct vpmadt032_cmd *cmd;
cmd = vpmadt032_getreg_full_async(vpm, pagechange, addr);
if (unlikely(!cmd)) {
return -ENOMEM;
}
return vpmadt032_getreg_full_return(vpm, pagechange, addr, outbuf, cmd);
}
static int vpmadt032_setreg_full(struct vpmadt032 *vpm, int pagechange, unsigned int addr,
u16 data)
{
unsigned long flags;
struct vpmadt032_cmd *cmd;
cmd = vpmadt032_get_free_cmd(vpm);
if (!cmd)
return -ENOMEM;
cmd->desc = cpu_to_le16((pagechange) ? (__VPM150M_WR|__VPM150M_RWPAGE) : __VPM150M_WR);
cmd->address = cpu_to_le16(addr);
cmd->data = cpu_to_le16(data);
spin_lock_irqsave(&vpm->list_lock, flags);
list_add_tail(&cmd->node, &vpm->pending_cmds);
spin_unlock_irqrestore(&vpm->list_lock, flags);
return 0;
}
static int vpmadt032_setpage(struct vpmadt032 *vpm, u16 addr)
{
addr &= 0xf;
/* We do not need to set the page if we're already on the page we're
* interested in. */
if (vpm->curpage == addr)
return 0;
else
vpm->curpage = addr;
return vpmadt032_setreg_full(vpm, 1, 0, addr);
}
static unsigned char vpmadt032_getpage(struct vpmadt032 *vpm)
{
unsigned short res;
const int pagechange = 1;
vpmadt032_getreg_full(vpm, pagechange, 0, &res);
return res;
}
static int vpmadt032_getreg(struct vpmadt032 *vpm, unsigned int addr, u16 *data)
{
unsigned short res;
vpmadt032_setpage(vpm, addr >> 16);
res = vpmadt032_getreg_full(vpm, 0, addr & 0xffff, data);
return res;
}
static int vpmadt032_setreg(struct vpmadt032 *vpm, unsigned int addr, u16 data)
{
int res;
vpmadt032_setpage(vpm, addr >> 16);
res = vpmadt032_setreg_full(vpm, 0, addr & 0xffff, data);
return res;
}
struct change_order {
struct list_head node;
unsigned int channel;
struct adt_lec_params params;
};
static struct change_order *alloc_change_order(void)
{
return kzalloc(sizeof(struct change_order), GFP_ATOMIC);
}
static void free_change_order(struct change_order *order)
{
kfree(order);
}
static int vpmadt032_control(struct vpmadt032 *vpm, unsigned short int channel,
GpakAlgCtrl_t control_code,
GPAK_AlgControlStat_t *pstatus)
{
gpakAlgControlStat_t stat;
int retry = 4;
while (retry--) {
stat = gpakAlgControl(vpm->dspid, channel,
control_code, pstatus);
if (AcDspCommFailure == stat)
msleep(5);
else
break;
}
return stat;
}
static int vpmadt032_enable_ec(struct vpmadt032 *vpm, int channel,
enum adt_companding companding)
{
int res;
GPAK_AlgControlStat_t pstatus;
GpakAlgCtrl_t control;
control = (ADT_COMP_ALAW == companding) ? EnableALawSwCompanding :
EnableMuLawSwCompanding;
if (vpm->options.debug & DEBUG_VPMADT032_ECHOCAN) {
const char *law;
law = (control == EnableMuLawSwCompanding) ? "MuLaw" : "ALaw";
vpm_info(vpm, "Enabling ecan on channel: %d (%s)\n",
channel, law);
}
res = vpmadt032_control(vpm, channel, control, &pstatus);
if (res) {
vpm_info(vpm, "Unable to set SW Companding on "
"channel %d (reason %d)\n", channel, res);
}
res = vpmadt032_control(vpm, channel, EnableEcanA, &pstatus);
return res;
}
static int vpmadt032_disable_ec(struct vpmadt032 *vpm, int channel)
{
int res;
GPAK_AlgControlStat_t pstatus;
if (vpm->options.debug & DEBUG_VPMADT032_ECHOCAN)
vpm_info(vpm, "Disabling ecan on channel: %d\n", channel);
res = vpmadt032_control(vpm, channel, BypassSwCompanding, &pstatus);
if (res) {
vpm_info(vpm, "Unable to disable sw companding on "
"echo cancellation channel %d (reason %d)\n",
channel, res);
}
res = vpmadt032_control(vpm, channel, BypassEcanA, &pstatus);
return res;
}
static struct change_order *get_next_order(struct vpmadt032 *vpm)
{
struct change_order *order;
spin_lock(&vpm->change_list_lock);
if (!list_empty(&vpm->change_list)) {
order = list_entry(vpm->change_list.next,
struct change_order, node);
list_del(&order->node);
} else {
order = NULL;
}
spin_unlock(&vpm->change_list_lock);
return order;
}
static int nlp_settings_changed(const struct adt_lec_params *a,
const struct adt_lec_params *b)
{
return ((a->nlp_type != b->nlp_type) ||
(a->nlp_threshold != b->nlp_threshold) ||
(a->nlp_max_suppress != b->nlp_max_suppress));
}
static int echocan_on(const struct adt_lec_params *new,
const struct adt_lec_params *old)
{
return ((new->tap_length != old->tap_length) &&
(new->tap_length > 0));
}
static int echocan_off(const struct adt_lec_params *new,
const struct adt_lec_params *old)
{
return ((new->tap_length != old->tap_length) &&
(0 == new->tap_length));
}
static void update_channel_config(struct vpmadt032 *vpm, unsigned int channel,
struct adt_lec_params *desired)
{
int res;
GPAK_AlgControlStat_t pstatus;
GPAK_ChannelConfigStat_t cstatus;
GPAK_TearDownChanStat_t tstatus;
GpakChannelConfig_t chanconfig;
if (vpm->options.debug & DEBUG_VPMADT032_ECHOCAN) {
vpm_info(vpm, "Reconfiguring chan %d for nlp %d, "
"nlp_thresh %d, and max_supp %d\n", channel + 1,
desired->nlp_type, desired->nlp_threshold,
desired->nlp_max_suppress);
}
vpm->setchanconfig_from_state(vpm, channel, &chanconfig);
res = gpakTearDownChannel(vpm->dspid, channel, &tstatus);
if (res)
return;
res = gpakConfigureChannel(vpm->dspid, channel, tdmToTdm,
&chanconfig, &cstatus);
if (res)
return;
if (!desired->tap_length) {
res = vpmadt032_control(vpm, channel,
BypassSwCompanding, &pstatus);
if (res) {
vpm_info(vpm, "Unable to disable sw companding on "
"echo cancellation channel %d (reason %d)\n",
channel, res);
}
vpmadt032_control(vpm, channel, BypassEcanA, &pstatus);
}
return;
}
/**
* vpmadt032_bh - Changes the echocan parameters on the vpmadt032 module.
*
* This function is typically scheduled to run in the workqueue by the
* vpmadt032_echocan_with_params function. This is because communicating with
* the hardware can take some time while messages are sent to the VPMADT032
* module and the driver waits for the responses.
*/
#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 20)
static void vpmadt032_bh(void *data)
{
struct vpmadt032 *vpm = data;
#else
static void vpmadt032_bh(struct work_struct *data)
{
struct vpmadt032 *vpm = container_of(data, struct vpmadt032, work);
#endif
struct change_order *order;
while ((order = get_next_order(vpm))) {
struct adt_lec_params *old;
struct adt_lec_params *new;
unsigned int channel;
channel = order->channel;
BUG_ON(channel >= ARRAY_SIZE(vpm->curecstate));
old = &vpm->curecstate[channel];
new = &order->params;
if (nlp_settings_changed(new, old))
update_channel_config(vpm, channel, new);
else if (echocan_on(new, old))
vpmadt032_enable_ec(vpm, channel, new->companding);
else if (echocan_off(new, old))
vpmadt032_disable_ec(vpm, channel);
*old = order->params;
free_change_order(order);
}
}
#include "adt_lec.c"
static void vpmadt032_check_and_schedule_update(struct vpmadt032 *vpm,
struct change_order *order)
{
struct change_order *cur;
struct change_order *n;
INIT_LIST_HEAD(&order->node);
spin_lock(&vpm->change_list_lock);
list_for_each_entry_safe(cur, n, &vpm->change_list, node) {
if (cur->channel == order->channel) {
list_replace(&cur->node, &order->node);
free_change_order(cur);
break;
}
}
if (list_empty(&order->node))
list_add_tail(&order->node, &vpm->change_list);
spin_unlock(&vpm->change_list_lock);
queue_work(vpm->wq, &vpm->work);
}
int vpmadt032_echocan_create(struct vpmadt032 *vpm, int channo,
enum adt_companding companding,
struct dahdi_echocanparams *ecp,
struct dahdi_echocanparam *p)
{
unsigned int ret;
struct change_order *order = alloc_change_order();
if (!order)
return -ENOMEM;
memcpy(&order->params, &vpm->curecstate[channo], sizeof(order->params));
ret = adt_lec_parse_params(&order->params, ecp, p);
if (ret) {
free_change_order(order);
return ret;
}
if (vpm->options.debug & DEBUG_VPMADT032_ECHOCAN) {
vpm_info(vpm, "Channel is %d length %d\n",
channo, ecp->tap_length);
}
/* The driver cannot control the number of taps on the VPMADT032
* module. Instead, it uses tap_length to enable or disable the echo
* cancellation. */
order->params.tap_length = (ecp->tap_length) ? 1 : 0;
order->params.companding = companding;
order->channel = channo;
vpmadt032_check_and_schedule_update(vpm, order);
return 0;
}
EXPORT_SYMBOL(vpmadt032_echocan_create);
void vpmadt032_echocan_free(struct vpmadt032 *vpm, int channo,
struct dahdi_echocan_state *ec)
{
struct change_order *order;
order = alloc_change_order();
WARN_ON(!order);
if (!order)
return;
adt_lec_init_defaults(&order->params, 0);
order->params.nlp_type = vpm->options.vpmnlptype;
order->params.nlp_threshold = vpm->options.vpmnlpthresh;
order->params.nlp_max_suppress = vpm->options.vpmnlpmaxsupp;
order->channel = channo;
if (vpm->options.debug & DEBUG_VPMADT032_ECHOCAN)
vpm_info(vpm, "Channel is %d length 0\n", channo);
vpmadt032_check_and_schedule_update(vpm, order);
}
EXPORT_SYMBOL(vpmadt032_echocan_free);
struct vpmadt032 *
vpmadt032_alloc(struct vpmadt032_options *options, const char *board_name)
{
struct vpmadt032 *vpm;
int i;
const char *suffix = "-vpm";
size_t length;
might_sleep();
length = strlen(board_name) + strlen(suffix) + 1;
/* Add a little extra to store the wq_name. */
vpm = kzalloc(sizeof(*vpm) + length, GFP_KERNEL);
if (!vpm)
return NULL;
#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 18)
/* There is a limit on the length of the name of the workqueue. */
strcpy(vpm->wq_name, "vpmadt032");
#else
strcpy(vpm->wq_name, board_name);
strcat(vpm->wq_name, suffix);
#endif
/* Init our vpmadt032 struct */
memcpy(&vpm->options, options, sizeof(*options));
spin_lock_init(&vpm->list_lock);
spin_lock_init(&vpm->change_list_lock);
INIT_LIST_HEAD(&vpm->change_list);
INIT_LIST_HEAD(&vpm->pending_cmds);
INIT_LIST_HEAD(&vpm->active_cmds);
sema_init(&vpm->sem, 1);
vpm->curpage = 0x80;
vpm->dspid = -1;
/* Do not use the global workqueue for processing these events. Some of
* the operations can take 100s of ms, most of that time spent sleeping.
* On single CPU systems, this unduly serializes operations accross
* multiple vpmadt032 instances. */
vpm->wq = create_singlethread_workqueue(vpm->wq_name);
if (!vpm->wq) {
kfree(vpm);
return NULL;
}
/* Place this structure in the ifaces array so that the DspId from the
* Gpak Library can be used to locate it. */
write_lock(&ifacelock);
for (i=0; i<MAX_DSP_CORES; ++i) {
if (NULL == ifaces[i]) {
ifaces[i] = vpm;
vpm->dspid = i;
break;
}
}
write_unlock(&ifacelock);
if (-1 == vpm->dspid) {
kfree(vpm);
dev_notice(&vpm->vb->pdev->dev, "Unable to initialize another vpmadt032 modules\n");
vpm = NULL;
}
return vpm;
}
EXPORT_SYMBOL(vpmadt032_alloc);
int vpmadt032_reset(struct vpmadt032 *vpm)
{
int res;
gpakPingDspStat_t pingstatus;
u16 version;
unsigned long stoptime;
struct device *const dev = &vpm->vb->pdev->dev;
might_sleep();
set_bit(VPM150M_HPIRESET, &vpm->control);
msleep(2000);
/* It should never take longer than 5 seconds. */
stoptime = jiffies + 3*HZ;
while (test_bit(VPM150M_HPIRESET, &vpm->control) &&
time_before(jiffies, stoptime))
msleep(1);
if (time_after(jiffies, stoptime)) {
dev_dbg(dev, "Detected failure to clear HPIRESET.\n");
return -EIO;
}
/* Set us up to page 0 */
vpmadt032_setpage(vpm, 0);
res = vpmadtreg_loadfirmware(vpm->vb);
if (res) {
dev_err(dev, "Failed to load VPMADT032 firmware.\n");
return res;
}
vpm->curpage = -1;
stoptime = jiffies + 3*HZ;
set_bit(VPM150M_SWRESET, &vpm->control);
while (test_bit(VPM150M_SWRESET, &vpm->control) &&
time_before(jiffies, stoptime))
msleep(1);
if (time_after(jiffies, stoptime)) {
dev_dbg(dev, "Detected failure to clear SWRESET.\n");
return -EIO;
}
/* Set us up to page 0 */
pingstatus = gpakPingDsp(vpm->dspid, &version);
if (!pingstatus) {
vpm_info(vpm, "VPM present and operational "
"(Firmware version %x)\n", version);
vpm->version = version;
res = 0;
} else {
res = -EIO;
}
return res;
}
EXPORT_SYMBOL(vpmadt032_reset);
int
vpmadt032_init(struct vpmadt032 *vpm, struct voicebus *vb)
{
int i;
u16 reg;
int res = -EFAULT;
unsigned long stoptime;
struct device *dev;
gpakPingDspStat_t pingstatus;
BUG_ON(!vpm->setchanconfig_from_state);
BUG_ON(!vpm->wq);
BUG_ON(!vb);
vpm->vb = vb;
might_sleep();
dev = &vpm->vb->pdev->dev;
#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 20)
INIT_WORK(&vpm->work, vpmadt032_bh, vpm);
#else
INIT_WORK(&vpm->work, vpmadt032_bh);
#endif
if (vpm->options.debug & DEBUG_VPMADT032_ECHOCAN)
dev_info(dev, "VPMADT032 Testing page access: ");
for (i = 0; i < 0xf; i++) {
int x;
for (x = 0; x < 3; x++) {
vpmadt032_setpage(vpm, i);
reg = vpmadt032_getpage(vpm);
if (reg != i) {
if (vpm->options.debug &
DEBUG_VPMADT032_ECHOCAN) {
dev_err(dev, "Failed: Sent %x != %x " \
"VPMADT032 Failed HI page " \
"test\n", i, reg);
}
res = -ENODEV;
goto failed_exit;
}
}
}
if (vpm->options.debug & DEBUG_VPMADT032_ECHOCAN)
dev_info(&vpm->vb->pdev->dev, "Passed\n");
stoptime = jiffies + 3*HZ;
set_bit(VPM150M_HPIRESET, &vpm->control);
while (test_bit(VPM150M_HPIRESET, &vpm->control) &&
time_before(jiffies, stoptime))
msleep(1);
if (time_after(jiffies, stoptime)) {
dev_dbg(dev, "Detected failure to clear HPIRESET.\n");
res = -EIO;
goto failed_exit;
}
msleep(250);
/* Set us up to page 0 */
vpmadt032_setpage(vpm, 0);
if (vpm->options.debug & DEBUG_VPMADT032_ECHOCAN)
dev_info(&vpm->vb->pdev->dev, "VPMADT032 now doing address test: ");
for (i = 0; i < 16; i++) {
int x;
for (x = 0; x < 2; x++) {
vpmadt032_setreg(vpm, 0x1000, i);
vpmadt032_getreg(vpm, 0x1000, &reg);
if (reg != i) {
printk(KERN_CONT "VPMADT032 Failed address test\n");
res = -EIO;
}
}
}
if (vpm->options.debug & DEBUG_VPMADT032_ECHOCAN)
printk(KERN_CONT "Passed\n");
stoptime = jiffies + 3*HZ;
set_bit(VPM150M_HPIRESET, &vpm->control);
while (test_bit(VPM150M_HPIRESET, &vpm->control) &&
time_before(jiffies, stoptime))
msleep(1);
if (time_after(jiffies, stoptime)) {
dev_dbg(dev, "Detected failure to clear SWRESET.\n");
res = -EIO;
goto failed_exit;
}
res = vpmadtreg_loadfirmware(vb);
if (res) {
dev_info(&vb->pdev->dev, "Failed to load the firmware.\n");
return res;
}
vpm->curpage = -1;
dev_info(&vb->pdev->dev, "Booting VPMADT032\n");
stoptime = jiffies + 3*HZ;
set_bit(VPM150M_SWRESET, &vpm->control);
while (test_bit(VPM150M_SWRESET, &vpm->control) &&
time_before(jiffies, stoptime))
msleep(1);
if (time_after(jiffies, stoptime)) {
dev_dbg(dev, "Detected failure to clear SWRESET.\n");
res = -EIO;
goto failed_exit;
}
pingstatus = gpakPingDsp(vpm->dspid, &vpm->version);
if (!pingstatus) {
dev_info(&vb->pdev->dev, "VPM present and operational "
"(Firmware version %x)\n", vpm->version);
} else {
dev_notice(&vpm->vb->pdev->dev, "VPMADT032 Failed! Unable to ping the DSP (%d)!\n", pingstatus);
res = -EIO;
goto failed_exit;
}
return 0;
failed_exit:
return res;
}
EXPORT_SYMBOL(vpmadt032_init);
void vpmadt032_get_default_parameters(struct GpakEcanParms *p)
{
memset(p, 0, sizeof(*p));
p->EcanTapLength = 1024;
p->EcanNlpType = DEFAULT_NLPTYPE;
p->EcanAdaptEnable = 1;
p->EcanG165DetEnable = 1;
p->EcanDblTalkThresh = 6;
p->EcanMaxDoubleTalkThres = 40;
p->EcanNlpThreshold = DEFAULT_NLPTHRESH;
p->EcanNlpConv = 18;
p->EcanNlpUnConv = 12;
p->EcanNlpMaxSuppress = DEFAULT_NLPMAXSUPP;
p->EcanCngThreshold = 43;
p->EcanAdaptLimit = 25;
p->EcanCrossCorrLimit = 15;
p->EcanNumFirSegments = 3;
p->EcanFirSegmentLen = 48;
p->EcanReconvergenceCheckEnable = 1;
p->EcanTandemOperationEnable = 0;
p->EcanMixedFourWireMode = 0;
p->EcanSaturationLevel = 3;
p->EcanNLPSaturationThreshold = 6;
}
EXPORT_SYMBOL(vpmadt032_get_default_parameters);
void vpmadt032_free(struct vpmadt032 *vpm)
{
unsigned long flags;
struct vpmadt032_cmd *cmd;
struct change_order *order;
LIST_HEAD(local_list);
if (!vpm)
return;
BUG_ON(!vpm->wq);
destroy_workqueue(vpm->wq);
/* Move all the commands onto the local list protected by the locks */
spin_lock_irqsave(&vpm->list_lock, flags);
list_splice(&vpm->pending_cmds, &local_list);
list_splice(&vpm->active_cmds, &local_list);
spin_unlock_irqrestore(&vpm->list_lock, flags);
while (!list_empty(&local_list)) {
cmd = list_entry(local_list.next, struct vpmadt032_cmd, node);
list_del(&cmd->node);
kfree(cmd);
}
spin_lock(&vpm->change_list_lock);
list_splice(&vpm->change_list, &local_list);
spin_unlock(&vpm->change_list_lock);
while (!list_empty(&local_list)) {
order = list_entry(local_list.next, struct change_order, node);
list_del(&order->node);
kfree(order);
}
BUG_ON(ifaces[vpm->dspid] != vpm);
write_lock(&ifacelock);
ifaces[vpm->dspid] = NULL;
write_unlock(&ifacelock);
kfree(vpm);
}
EXPORT_SYMBOL(vpmadt032_free);
int vpmadt032_module_init(void)
{
rwlock_init(&ifacelock);
memset(ifaces, 0, sizeof(ifaces));
return 0;
}
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
* gpakReadDspMemory - Read DSP memory.
*
* FUNCTION
* This function reads a contiguous block of words from DSP memory starting at
* the specified address.
*
* RETURNS
* nothing
*
*/
void gpakReadDspMemory(
unsigned short int DspId, /* DSP Identifier (0 to MAX_DSP_CORES-1) */
DSP_ADDRESS DspAddress, /* DSP's memory address of first word */
unsigned int NumWords, /* number of contiguous words to read */
DSP_WORD *pWordValues /* pointer to array of word values variable */
)
{
struct vpmadt032 *vpm = find_iface(DspId);
int i;
int ret;
vpmadt032_io_wait(vpm);
if ( NumWords < VPM150M_MAX_COMMANDS ) {
struct vpmadt032_cmd *cmds[VPM150M_MAX_COMMANDS];
memset(cmds, 0, sizeof(cmds));
vpmadt032_setpage(vpm, DspAddress >> 16);
DspAddress &= 0xffff;
for (i=0; i < NumWords; ++i) {
if (!(cmds[i] = vpmadt032_getreg_full_async(vpm,0,DspAddress+i))) {
return;
}
}
for (i=NumWords-1; i >=0; --i) {
ret = vpmadt032_getreg_full_return(vpm,0,DspAddress+i,&pWordValues[i],
cmds[i]);
if (0 != ret) {
return;
}
}
}
else {
for (i=0; i<NumWords; ++i) {
vpmadt032_getreg(vpm, DspAddress + i, &pWordValues[i]);
}
}
return;
}
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
* gpakWriteDspMemory - Write DSP memory.
*
* FUNCTION
* This function writes a contiguous block of words to DSP memory starting at
* the specified address.
*
* RETURNS
* nothing
*
*/
void gpakWriteDspMemory(
unsigned short int DspId, /* DSP Identifier (0 to MAX_DSP_CORES-1) */
DSP_ADDRESS DspAddress, /* DSP's memory address of first word */
unsigned int NumWords, /* number of contiguous words to write */
DSP_WORD *pWordValues /* pointer to array of word values to write */
)
{
struct vpmadt032 *vpm = find_iface(DspId);
int i;
//dev_info(&vpm->vb->pdev->dev, "Writing %d words to memory\n", NumWords);
if (vpm) {
for (i = 0; i < NumWords; ++i) {
vpmadt032_setreg(vpm, DspAddress + i, pWordValues[i]);
}
#if 0
for (i = 0; i < NumWords; i++) {
if (wctdm_vpmadt032_getreg(wc, DspAddress + i) != pWordValues[i]) {
dev_notice(&vpm->vb->pdev->dev, "Error in write. Address %x is not %x\n", DspAddress + i, pWordValues[i]);
}
}
#endif
}
return;
}
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
* gpakHostDelay - Delay for a fixed time interval.
*
* FUNCTION
* This function delays for a fixed time interval before returning. The time
* interval is the Host Port Interface sampling period when polling a DSP for
* replies to command messages.
*
* RETURNS
* nothing
*
*/
void gpakHostDelay(void)
{
}
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
* gpakLockAccess - Lock access to the specified DSP.
*
* FUNCTION
* This function aquires exclusive access to the specified DSP.
*
* RETURNS
* nothing
*
*/
void gpakLockAccess(unsigned short DspId)
{
struct vpmadt032 *vpm;
vpm = find_iface(DspId);
if (!vpm)
return;
down(&vpm->sem);
return;
}
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
* gpakUnlockAccess - Unlock access to the specified DSP.
*
* FUNCTION
* This function releases exclusive access to the specified DSP.
*
* RETURNS
* nothing
*
*/
void gpakUnlockAccess(unsigned short DspId)
{
struct vpmadt032 *vpm;
vpm = find_iface(DspId);
if (vpm) {
up(&vpm->sem);
}
}
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
* gpakReadFile - Read a block of bytes from a G.PAK Download file.
*
* FUNCTION
* This function reads a contiguous block of bytes from a G.PAK Download file
* starting at the current file position.
*
* RETURNS
* The number of bytes read from the file.
* -1 indicates an error occurred.
* 0 indicates all bytes have been read (end of file)
*
*/
int gpakReadFile(
GPAK_FILE_ID FileId, /* G.PAK Download File Identifier */
unsigned char *pBuffer, /* pointer to buffer for storing bytes */
unsigned int NumBytes /* number of bytes to read */
)
{
/* The firmware is loaded into the part by a closed-source firmware
* loader, and therefore this function should never be called. */
WARN_ON(1);
return -1;
}