Commit 719f82d3 authored by Eliot Blennerhassett's avatar Eliot Blennerhassett Committed by Takashi Iwai

ALSA: Add support of AudioScience ASI boards

Added the support of AudioScience ASI boards.
The driver has been tested for years on alsa-driver external tree,
now finally got merged to the kernel.
Signed-off-by: default avatarEliot Blennerhassett <eblennerhassett@audioscience.com>
Signed-off-by: default avatarTakashi Iwai <tiwai@suse.de>
parent cf0dbba5
......@@ -227,6 +227,16 @@ Prior to version 0.9.0rc4 options had a 'snd_' prefix. This was removed.
The power-management is supported.
Module snd-asihpi
-----------------
Module for AudioScience ASI soundcards
enable_hpi_hwdep - enable HPI hwdep for AudioScience soundcard
This module supports multiple cards.
The driver requires the firmware loader support on kernel.
Module snd-atiixp
-----------------
......
......@@ -58,6 +58,18 @@ config SND_ALI5451
To compile this driver as a module, choose M here: the module
will be called snd-ali5451.
config SND_ASIHPI
tristate "AudioScience ASIxxxx"
depends on X86
select FW_LOADER
select SND_PCM
select SND_HWDEP
help
Say Y here to include support for AudioScience ASI sound cards.
To compile this driver as a module, choose M here: the module
will be called snd-asihpi.
config SND_ATIIXP
tristate "ATI IXP AC97 Controller"
select SND_AC97_CODEC
......
......@@ -57,6 +57,7 @@ obj-$(CONFIG_SND_VIA82XX_MODEM) += snd-via82xx-modem.o
obj-$(CONFIG_SND) += \
ac97/ \
ali5451/ \
asihpi/ \
au88x0/ \
aw2/ \
ctxfi/ \
......
snd-asihpi-objs := asihpi.o hpioctl.o hpimsginit.o\
hpicmn.o hpifunc.o hpidebug.o hpidspcd.o\
hpios.o hpi6000.o hpi6205.o hpimsgx.o
obj-$(CONFIG_SND_ASIHPI) += snd-asihpi.o
/*
* Asihpi soundcard
* Copyright (c) by AudioScience Inc <alsa@audioscience.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of version 2 of the GNU General Public License as
* published by the Free Software Foundation;
*
* 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
*
* The following is not a condition of use, merely a request:
* If you modify this program, particularly if you fix errors, AudioScience Inc
* would appreciate it if you grant us the right to use those modifications
* for any purpose including commercial applications.
*/
/* >0: print Hw params, timer vars. >1: print stream write/copy sizes */
#define REALLY_VERBOSE_LOGGING 0
#if REALLY_VERBOSE_LOGGING
#define VPRINTK1 snd_printd
#else
#define VPRINTK1(...)
#endif
#if REALLY_VERBOSE_LOGGING > 1
#define VPRINTK2 snd_printd
#else
#define VPRINTK2(...)
#endif
#ifndef ASI_STYLE_NAMES
/* not sure how ALSA style name should look */
#define ASI_STYLE_NAMES 1
#endif
#include "hpi_internal.h"
#include "hpimsginit.h"
#include "hpioctl.h"
#include <linux/pci.h>
#include <linux/init.h>
#include <linux/jiffies.h>
#include <linux/slab.h>
#include <linux/time.h>
#include <linux/wait.h>
#include <sound/core.h>
#include <sound/control.h>
#include <sound/pcm.h>
#include <sound/pcm_params.h>
#include <sound/info.h>
#include <sound/initval.h>
#include <sound/tlv.h>
#include <sound/hwdep.h>
MODULE_LICENSE("GPL");
MODULE_AUTHOR("AudioScience inc. <support@audioscience.com>");
MODULE_DESCRIPTION("AudioScience ALSA ASI5000 ASI6000 ASI87xx ASI89xx");
static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* index 0-MAX */
static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* ID for this card */
static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP;
static int enable_hpi_hwdep = 1;
module_param_array(index, int, NULL, S_IRUGO);
MODULE_PARM_DESC(index, "ALSA index value for AudioScience soundcard.");
module_param_array(id, charp, NULL, S_IRUGO);
MODULE_PARM_DESC(id, "ALSA ID string for AudioScience soundcard.");
module_param_array(enable, bool, NULL, S_IRUGO);
MODULE_PARM_DESC(enable, "ALSA enable AudioScience soundcard.");
module_param(enable_hpi_hwdep, bool, S_IRUGO|S_IWUSR);
MODULE_PARM_DESC(enable_hpi_hwdep,
"ALSA enable HPI hwdep for AudioScience soundcard ");
/* identify driver */
#ifdef KERNEL_ALSA_BUILD
static char *build_info = "built using headers from kernel source";
module_param(build_info, charp, S_IRUGO);
MODULE_PARM_DESC(build_info, "built using headers from kernel source");
#else
static char *build_info = "built within ALSA source";
module_param(build_info, charp, S_IRUGO);
MODULE_PARM_DESC(build_info, "built within ALSA source");
#endif
/* set to 1 to dump every control from adapter to log */
static const int mixer_dump;
#define DEFAULT_SAMPLERATE 44100
static int adapter_fs = DEFAULT_SAMPLERATE;
static struct hpi_hsubsys *ss; /* handle to HPI audio subsystem */
/* defaults */
#define PERIODS_MIN 2
#define PERIOD_BYTES_MIN 2304
#define BUFFER_BYTES_MAX (512 * 1024)
/*#define TIMER_MILLISECONDS 20
#define FORCE_TIMER_JIFFIES ((TIMER_MILLISECONDS * HZ + 999)/1000)
*/
#define MAX_CLOCKSOURCES (HPI_SAMPLECLOCK_SOURCE_LAST + 1 + 7)
struct clk_source {
int source;
int index;
char *name;
};
struct clk_cache {
int count;
int has_local;
struct clk_source s[MAX_CLOCKSOURCES];
};
/* Per card data */
struct snd_card_asihpi {
struct snd_card *card;
struct pci_dev *pci;
u16 adapter_index;
u32 serial_number;
u16 type;
u16 version;
u16 num_outstreams;
u16 num_instreams;
u32 h_mixer;
struct clk_cache cc;
u16 support_mmap;
u16 support_grouping;
u16 support_mrx;
u16 update_interval_frames;
u16 in_max_chans;
u16 out_max_chans;
};
/* Per stream data */
struct snd_card_asihpi_pcm {
struct timer_list timer;
unsigned int respawn_timer;
unsigned int hpi_buffer_attached;
unsigned int pcm_size;
unsigned int pcm_count;
unsigned int bytes_per_sec;
unsigned int pcm_irq_pos; /* IRQ position */
unsigned int pcm_buf_pos; /* position in buffer */
struct snd_pcm_substream *substream;
u32 h_stream;
struct hpi_format format;
};
/* universal stream verbs work with out or in stream handles */
/* Functions to allow driver to give a buffer to HPI for busmastering */
static u16 hpi_stream_host_buffer_attach(
struct hpi_hsubsys *hS,
u32 h_stream, /* handle to outstream. */
u32 size_in_bytes, /* size in bytes of bus mastering buffer */
u32 pci_address
)
{
struct hpi_message hm;
struct hpi_response hr;
unsigned int obj = hpi_handle_object(h_stream);
if (!h_stream)
return HPI_ERROR_INVALID_OBJ;
hpi_init_message_response(&hm, &hr, obj,
obj == HPI_OBJ_OSTREAM ?
HPI_OSTREAM_HOSTBUFFER_ALLOC :
HPI_ISTREAM_HOSTBUFFER_ALLOC);
hpi_handle_to_indexes(h_stream, &hm.adapter_index,
&hm.obj_index);
hm.u.d.u.buffer.buffer_size = size_in_bytes;
hm.u.d.u.buffer.pci_address = pci_address;
hm.u.d.u.buffer.command = HPI_BUFFER_CMD_INTERNAL_GRANTADAPTER;
hpi_send_recv(&hm, &hr);
return hr.error;
}
static u16 hpi_stream_host_buffer_detach(
struct hpi_hsubsys *hS,
u32 h_stream
)
{
struct hpi_message hm;
struct hpi_response hr;
unsigned int obj = hpi_handle_object(h_stream);
if (!h_stream)
return HPI_ERROR_INVALID_OBJ;
hpi_init_message_response(&hm, &hr, obj,
obj == HPI_OBJ_OSTREAM ?
HPI_OSTREAM_HOSTBUFFER_FREE :
HPI_ISTREAM_HOSTBUFFER_FREE);
hpi_handle_to_indexes(h_stream, &hm.adapter_index,
&hm.obj_index);
hm.u.d.u.buffer.command = HPI_BUFFER_CMD_INTERNAL_REVOKEADAPTER;
hpi_send_recv(&hm, &hr);
return hr.error;
}
static inline u16 hpi_stream_start(struct hpi_hsubsys *hS, u32 h_stream)
{
if (hpi_handle_object(h_stream) == HPI_OBJ_OSTREAM)
return hpi_outstream_start(hS, h_stream);
else
return hpi_instream_start(hS, h_stream);
}
static inline u16 hpi_stream_stop(struct hpi_hsubsys *hS, u32 h_stream)
{
if (hpi_handle_object(h_stream) == HPI_OBJ_OSTREAM)
return hpi_outstream_stop(hS, h_stream);
else
return hpi_instream_stop(hS, h_stream);
}
static inline u16 hpi_stream_get_info_ex(
struct hpi_hsubsys *hS,
u32 h_stream,
u16 *pw_state,
u32 *pbuffer_size,
u32 *pdata_in_buffer,
u32 *psample_count,
u32 *pauxiliary_data
)
{
if (hpi_handle_object(h_stream) == HPI_OBJ_OSTREAM)
return hpi_outstream_get_info_ex(hS, h_stream, pw_state,
pbuffer_size, pdata_in_buffer,
psample_count, pauxiliary_data);
else
return hpi_instream_get_info_ex(hS, h_stream, pw_state,
pbuffer_size, pdata_in_buffer,
psample_count, pauxiliary_data);
}
static inline u16 hpi_stream_group_add(struct hpi_hsubsys *hS,
u32 h_master,
u32 h_stream)
{
if (hpi_handle_object(h_master) == HPI_OBJ_OSTREAM)
return hpi_outstream_group_add(hS, h_master, h_stream);
else
return hpi_instream_group_add(hS, h_master, h_stream);
}
static inline u16 hpi_stream_group_reset(struct hpi_hsubsys *hS,
u32 h_stream)
{
if (hpi_handle_object(h_stream) == HPI_OBJ_OSTREAM)
return hpi_outstream_group_reset(hS, h_stream);
else
return hpi_instream_group_reset(hS, h_stream);
}
static inline u16 hpi_stream_group_get_map(struct hpi_hsubsys *hS,
u32 h_stream, u32 *mo, u32 *mi)
{
if (hpi_handle_object(h_stream) == HPI_OBJ_OSTREAM)
return hpi_outstream_group_get_map(hS, h_stream, mo, mi);
else
return hpi_instream_group_get_map(hS, h_stream, mo, mi);
}
static u16 handle_error(u16 err, int line, char *filename)
{
if (err)
printk(KERN_WARNING
"in file %s, line %d: HPI error %d\n",
filename, line, err);
return err;
}
#define hpi_handle_error(x) handle_error(x, __LINE__, __FILE__)
/***************************** GENERAL PCM ****************/
#if REALLY_VERBOSE_LOGGING
static void print_hwparams(struct snd_pcm_hw_params *p)
{
snd_printd("HWPARAMS \n");
snd_printd("samplerate %d \n", params_rate(p));
snd_printd("channels %d \n", params_channels(p));
snd_printd("format %d \n", params_format(p));
snd_printd("subformat %d \n", params_subformat(p));
snd_printd("buffer bytes %d \n", params_buffer_bytes(p));
snd_printd("period bytes %d \n", params_period_bytes(p));
snd_printd("access %d \n", params_access(p));
snd_printd("period_size %d \n", params_period_size(p));
snd_printd("periods %d \n", params_periods(p));
snd_printd("buffer_size %d \n", params_buffer_size(p));
}
#else
#define print_hwparams(x)
#endif
static snd_pcm_format_t hpi_to_alsa_formats[] = {
-1, /* INVALID */
SNDRV_PCM_FORMAT_U8, /* HPI_FORMAT_PCM8_UNSIGNED 1 */
SNDRV_PCM_FORMAT_S16, /* HPI_FORMAT_PCM16_SIGNED 2 */
-1, /* HPI_FORMAT_MPEG_L1 3 */
SNDRV_PCM_FORMAT_MPEG, /* HPI_FORMAT_MPEG_L2 4 */
SNDRV_PCM_FORMAT_MPEG, /* HPI_FORMAT_MPEG_L3 5 */
-1, /* HPI_FORMAT_DOLBY_AC2 6 */
-1, /* HPI_FORMAT_DOLBY_AC3 7 */
SNDRV_PCM_FORMAT_S16_BE,/* HPI_FORMAT_PCM16_BIGENDIAN 8 */
-1, /* HPI_FORMAT_AA_TAGIT1_HITS 9 */
-1, /* HPI_FORMAT_AA_TAGIT1_INSERTS 10 */
SNDRV_PCM_FORMAT_S32, /* HPI_FORMAT_PCM32_SIGNED 11 */
-1, /* HPI_FORMAT_RAW_BITSTREAM 12 */
-1, /* HPI_FORMAT_AA_TAGIT1_HITS_EX1 13 */
SNDRV_PCM_FORMAT_FLOAT, /* HPI_FORMAT_PCM32_FLOAT 14 */
#if 1
/* ALSA can't handle 3 byte sample size together with power-of-2
* constraint on buffer_bytes, so disable this format
*/
-1
#else
/* SNDRV_PCM_FORMAT_S24_3LE */ /* { HPI_FORMAT_PCM24_SIGNED 15 */
#endif
};
static int snd_card_asihpi_format_alsa2hpi(snd_pcm_format_t alsa_format,
u16 *hpi_format)
{
u16 format;
for (format = HPI_FORMAT_PCM8_UNSIGNED;
format <= HPI_FORMAT_PCM24_SIGNED; format++) {
if (hpi_to_alsa_formats[format] == alsa_format) {
*hpi_format = format;
return 0;
}
}
snd_printd(KERN_WARNING "failed match for alsa format %d\n",
alsa_format);
*hpi_format = 0;
return -EINVAL;
}
static void snd_card_asihpi_pcm_samplerates(struct snd_card_asihpi *asihpi,
struct snd_pcm_hardware *pcmhw)
{
u16 err;
u32 h_control;
u32 sample_rate;
int idx;
unsigned int rate_min = 200000;
unsigned int rate_max = 0;
unsigned int rates = 0;
if (asihpi->support_mrx) {
rates |= SNDRV_PCM_RATE_CONTINUOUS;
rates |= SNDRV_PCM_RATE_8000_96000;
rate_min = 8000;
rate_max = 100000;
} else {
/* on cards without SRC,
valid rates are determined by sampleclock */
err = hpi_mixer_get_control(ss, asihpi->h_mixer,
HPI_SOURCENODE_CLOCK_SOURCE, 0, 0, 0,
HPI_CONTROL_SAMPLECLOCK, &h_control);
if (err) {
snd_printk(KERN_ERR
"no local sampleclock, err %d\n", err);
}
for (idx = 0; idx < 100; idx++) {
if (hpi_sample_clock_query_local_rate(ss,
h_control, idx, &sample_rate)) {
if (!idx)
snd_printk(KERN_ERR
"local rate query failed\n");
break;
}
rate_min = min(rate_min, sample_rate);
rate_max = max(rate_max, sample_rate);
switch (sample_rate) {
case 5512:
rates |= SNDRV_PCM_RATE_5512;
break;
case 8000:
rates |= SNDRV_PCM_RATE_8000;
break;
case 11025:
rates |= SNDRV_PCM_RATE_11025;
break;
case 16000:
rates |= SNDRV_PCM_RATE_16000;
break;
case 22050:
rates |= SNDRV_PCM_RATE_22050;
break;
case 32000:
rates |= SNDRV_PCM_RATE_32000;
break;
case 44100:
rates |= SNDRV_PCM_RATE_44100;
break;
case 48000:
rates |= SNDRV_PCM_RATE_48000;
break;
case 64000:
rates |= SNDRV_PCM_RATE_64000;
break;
case 88200:
rates |= SNDRV_PCM_RATE_88200;
break;
case 96000:
rates |= SNDRV_PCM_RATE_96000;
break;
case 176400:
rates |= SNDRV_PCM_RATE_176400;
break;
case 192000:
rates |= SNDRV_PCM_RATE_192000;
break;
default: /* some other rate */
rates |= SNDRV_PCM_RATE_KNOT;
}
}
}
/* printk(KERN_INFO "Supported rates %X %d %d\n",
rates, rate_min, rate_max); */
pcmhw->rates = rates;
pcmhw->rate_min = rate_min;
pcmhw->rate_max = rate_max;
}
static int snd_card_asihpi_pcm_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params)
{
struct snd_pcm_runtime *runtime = substream->runtime;
struct snd_card_asihpi_pcm *dpcm = runtime->private_data;
struct snd_card_asihpi *card = snd_pcm_substream_chip(substream);
int err;
u16 format;
unsigned int bytes_per_sec;
print_hwparams(params);
err = snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(params));
if (err < 0)
return err;
err = snd_card_asihpi_format_alsa2hpi(params_format(params), &format);
if (err)
return err;
VPRINTK1(KERN_INFO "format %d, %d chans, %d_hz\n",
format, params_channels(params),
params_rate(params));
hpi_handle_error(hpi_format_create(&dpcm->format,
params_channels(params),
format, params_rate(params), 0, 0));
if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) {
if (hpi_instream_reset(ss, dpcm->h_stream) != 0)
return -EINVAL;
if (hpi_instream_set_format(ss,
dpcm->h_stream, &dpcm->format) != 0)
return -EINVAL;
}
dpcm->hpi_buffer_attached = 0;
if (card->support_mmap) {
err = hpi_stream_host_buffer_attach(ss, dpcm->h_stream,
params_buffer_bytes(params), runtime->dma_addr);
if (err == 0) {
snd_printd(KERN_INFO
"stream_host_buffer_attach succeeded %u %lu\n",
params_buffer_bytes(params),
(unsigned long)runtime->dma_addr);
} else {
snd_printd(KERN_INFO
"stream_host_buffer_attach error %d\n",
err);
return -ENOMEM;
}
err = hpi_stream_get_info_ex(ss, dpcm->h_stream, NULL,
&dpcm->hpi_buffer_attached,
NULL, NULL, NULL);
snd_printd(KERN_INFO "stream_host_buffer_attach status 0x%x\n",
dpcm->hpi_buffer_attached);
}
bytes_per_sec = params_rate(params) * params_channels(params);
bytes_per_sec *= snd_pcm_format_width(params_format(params));
bytes_per_sec /= 8;
if (bytes_per_sec <= 0)
return -EINVAL;
dpcm->bytes_per_sec = bytes_per_sec;
dpcm->pcm_size = params_buffer_bytes(params);
dpcm->pcm_count = params_period_bytes(params);
snd_printd(KERN_INFO "pcm_size=%d, pcm_count=%d, bps=%d\n",
dpcm->pcm_size, dpcm->pcm_count, bytes_per_sec);
dpcm->pcm_irq_pos = 0;
dpcm->pcm_buf_pos = 0;
return 0;
}
static void snd_card_asihpi_pcm_timer_start(struct snd_pcm_substream *
substream)
{
struct snd_pcm_runtime *runtime = substream->runtime;
struct snd_card_asihpi_pcm *dpcm = runtime->private_data;
int expiry;
expiry = (dpcm->pcm_count * HZ / dpcm->bytes_per_sec);
/* wait longer the first time, for samples to propagate */
expiry = max(expiry, 20);
dpcm->timer.expires = jiffies + expiry;
dpcm->respawn_timer = 1;
add_timer(&dpcm->timer);
}
static void snd_card_asihpi_pcm_timer_stop(struct snd_pcm_substream *substream)
{
struct snd_pcm_runtime *runtime = substream->runtime;
struct snd_card_asihpi_pcm *dpcm = runtime->private_data;
dpcm->respawn_timer = 0;
del_timer(&dpcm->timer);
}
static int snd_card_asihpi_trigger(struct snd_pcm_substream *substream,
int cmd)
{
struct snd_card_asihpi_pcm *dpcm = substream->runtime->private_data;
struct snd_card_asihpi *card = snd_pcm_substream_chip(substream);
struct snd_pcm_substream *s;
u16 e;
snd_printd("trigger %dstream %d\n",
substream->stream, substream->number);
switch (cmd) {
case SNDRV_PCM_TRIGGER_START:
snd_pcm_group_for_each_entry(s, substream) {
struct snd_card_asihpi_pcm *ds;
ds = s->runtime->private_data;
if (snd_pcm_substream_chip(s) != card)
continue;
if ((s->stream == SNDRV_PCM_STREAM_PLAYBACK) &&
(card->support_mmap)) {
/* How do I know how much valid data is present
* in buffer? Just guessing 2 periods, but if
* buffer is bigger it may contain even more
* data??
*/
unsigned int preload = ds->pcm_count * 2;
VPRINTK2("preload %d\n", preload);
hpi_handle_error(hpi_outstream_write_buf(
ss, ds->h_stream,
&s->runtime->dma_area[0],
preload,
&ds->format));
}
if (card->support_grouping) {
VPRINTK1("\t_group %dstream %d\n", s->stream,
s->number);
e = hpi_stream_group_add(ss,
dpcm->h_stream,
ds->h_stream);
if (!e) {
snd_pcm_trigger_done(s, substream);
} else {
hpi_handle_error(e);
break;
}
} else
break;
}
snd_printd("start\n");
/* start the master stream */
snd_card_asihpi_pcm_timer_start(substream);
hpi_handle_error(hpi_stream_start(ss, dpcm->h_stream));
break;
case SNDRV_PCM_TRIGGER_STOP:
snd_card_asihpi_pcm_timer_stop(substream);
snd_pcm_group_for_each_entry(s, substream) {
if (snd_pcm_substream_chip(s) != card)
continue;
/*? workaround linked streams don't
transition to SETUP 20070706*/
s->runtime->status->state = SNDRV_PCM_STATE_SETUP;
if (card->support_grouping) {
VPRINTK1("\t_group %dstream %d\n", s->stream,
s->number);
snd_pcm_trigger_done(s, substream);
} else
break;
}
snd_printd("stop\n");
/* _prepare and _hwparams reset the stream */
hpi_handle_error(hpi_stream_stop(ss, dpcm->h_stream));
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
hpi_handle_error(
hpi_outstream_reset(ss, dpcm->h_stream));
if (card->support_grouping)
hpi_handle_error(hpi_stream_group_reset(ss,
dpcm->h_stream));
break;
case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
snd_printd("pause release\n");
hpi_handle_error(hpi_stream_start(ss, dpcm->h_stream));
snd_card_asihpi_pcm_timer_start(substream);
break;
case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
snd_printd("pause\n");
snd_card_asihpi_pcm_timer_stop(substream);
hpi_handle_error(hpi_stream_stop(ss, dpcm->h_stream));
break;
default:
snd_printd("\tINVALID\n");
return -EINVAL;
}
return 0;
}
static int
snd_card_asihpi_hw_free(struct snd_pcm_substream *substream)
{
struct snd_pcm_runtime *runtime = substream->runtime;
struct snd_card_asihpi_pcm *dpcm = runtime->private_data;
if (dpcm->hpi_buffer_attached)
hpi_stream_host_buffer_detach(ss, dpcm->h_stream);
snd_pcm_lib_free_pages(substream);
return 0;
}
static void snd_card_asihpi_runtime_free(struct snd_pcm_runtime *runtime)
{
struct snd_card_asihpi_pcm *dpcm = runtime->private_data;
kfree(dpcm);
}
/*algorithm outline
Without linking degenerates to getting single stream pos etc
Without mmap 2nd loop degenerates to snd_pcm_period_elapsed
*/
/*
buf_pos=get_buf_pos(s);
for_each_linked_stream(s) {
buf_pos=get_buf_pos(s);
min_buf_pos = modulo_min(min_buf_pos, buf_pos, pcm_size)
new_data = min(new_data, calc_new_data(buf_pos,irq_pos)
}
timer.expires = jiffies + predict_next_period_ready(min_buf_pos);
for_each_linked_stream(s) {
s->buf_pos = min_buf_pos;
if (new_data > pcm_count) {
if (mmap) {
irq_pos = (irq_pos + pcm_count) % pcm_size;
if (playback) {
write(pcm_count);
} else {
read(pcm_count);
}
}
snd_pcm_period_elapsed(s);
}
}
*/
/** Minimum of 2 modulo values. Works correctly when the difference between
* the values is less than half the modulus
*/
static inline unsigned int modulo_min(unsigned int a, unsigned int b,
unsigned long int modulus)
{
unsigned int result;
if (((a-b) % modulus) < (modulus/2))
result = b;
else
result = a;
return result;
}
/** Timer function, equivalent to interrupt service routine for cards
*/
static void snd_card_asihpi_timer_function(unsigned long data)
{
struct snd_card_asihpi_pcm *dpcm = (struct snd_card_asihpi_pcm *)data;
struct snd_card_asihpi *card = snd_pcm_substream_chip(dpcm->substream);
struct snd_pcm_runtime *runtime;
struct snd_pcm_substream *s;
unsigned int newdata = 0;
unsigned int buf_pos, min_buf_pos = 0;
unsigned int remdata, xfercount, next_jiffies;
int first = 1;
u16 state;
u32 buffer_size, data_avail, samples_played, aux;
/* find minimum newdata and buffer pos in group */
snd_pcm_group_for_each_entry(s, dpcm->substream) {
struct snd_card_asihpi_pcm *ds = s->runtime->private_data;
runtime = s->runtime;
if (snd_pcm_substream_chip(s) != card)
continue;
hpi_handle_error(hpi_stream_get_info_ex(ss,
ds->h_stream, &state,
&buffer_size, &data_avail,
&samples_played, &aux));
/* number of bytes in on-card buffer */
runtime->delay = aux;
if (state == HPI_STATE_DRAINED) {
snd_printd(KERN_WARNING "outstream %d drained\n",
s->number);
snd_pcm_stop(s, SNDRV_PCM_STATE_XRUN);
return;
}
if (s->stream == SNDRV_PCM_STREAM_PLAYBACK) {
buf_pos = frames_to_bytes(runtime, samples_played);
} else {
buf_pos = data_avail + ds->pcm_irq_pos;
}
if (first) {
/* can't statically init min when wrap is involved */
min_buf_pos = buf_pos;
newdata = (buf_pos - ds->pcm_irq_pos) % ds->pcm_size;
first = 0;
} else {
min_buf_pos =
modulo_min(min_buf_pos, buf_pos, UINT_MAX+1L);
newdata = min(
(buf_pos - ds->pcm_irq_pos) % ds->pcm_size,
newdata);
}
VPRINTK1("PB timer hw_ptr x%04lX, appl_ptr x%04lX\n",
(unsigned long)frames_to_bytes(runtime,
runtime->status->hw_ptr),
(unsigned long)frames_to_bytes(runtime,
runtime->control->appl_ptr));
VPRINTK1("%d S=%d, irq=%04X, pos=x%04X, left=x%04X,"
" aux=x%04X space=x%04X\n", s->number,
state, ds->pcm_irq_pos, buf_pos, (int)data_avail,
(int)aux, buffer_size-data_avail);
}
remdata = newdata % dpcm->pcm_count;
xfercount = newdata - remdata; /* a multiple of pcm_count */
next_jiffies = ((dpcm->pcm_count-remdata) * HZ / dpcm->bytes_per_sec)+1;
next_jiffies = max(next_jiffies, 2U * HZ / 1000U);
dpcm->timer.expires = jiffies + next_jiffies;
VPRINTK1("jif %d buf pos x%04X newdata x%04X xc x%04X\n",
next_jiffies, min_buf_pos, newdata, xfercount);
snd_pcm_group_for_each_entry(s, dpcm->substream) {
struct snd_card_asihpi_pcm *ds = s->runtime->private_data;
ds->pcm_buf_pos = min_buf_pos;
if (xfercount) {
if (card->support_mmap) {
ds->pcm_irq_pos = ds->pcm_irq_pos + xfercount;
if (s->stream == SNDRV_PCM_STREAM_PLAYBACK) {
VPRINTK2("write OS%d x%04x\n",
s->number,
ds->pcm_count);
hpi_handle_error(
hpi_outstream_write_buf(
ss, ds->h_stream,
&s->runtime->
dma_area[0],
xfercount,
&ds->format));
} else {
VPRINTK2("read IS%d x%04x\n",
s->number,
dpcm->pcm_count);
hpi_handle_error(
hpi_instream_read_buf(
ss, ds->h_stream,
NULL, xfercount));
}
} /* else R/W will be handled by read/write callbacks */
snd_pcm_period_elapsed(s);
}
}
if (dpcm->respawn_timer)
add_timer(&dpcm->timer);
}
/***************************** PLAYBACK OPS ****************/
static int snd_card_asihpi_playback_ioctl(struct snd_pcm_substream *substream,
unsigned int cmd, void *arg)
{
/* snd_printd(KERN_INFO "Playback ioctl %d\n", cmd); */
return snd_pcm_lib_ioctl(substream, cmd, arg);
}
static int snd_card_asihpi_playback_prepare(struct snd_pcm_substream *
substream)
{
struct snd_pcm_runtime *runtime = substream->runtime;
struct snd_card_asihpi_pcm *dpcm = runtime->private_data;
snd_printd(KERN_INFO "playback prepare %d\n", substream->number);
hpi_handle_error(hpi_outstream_reset(ss, dpcm->h_stream));
dpcm->pcm_irq_pos = 0;
dpcm->pcm_buf_pos = 0;
return 0;
}
static snd_pcm_uframes_t
snd_card_asihpi_playback_pointer(struct snd_pcm_substream *substream)
{
struct snd_pcm_runtime *runtime = substream->runtime;
struct snd_card_asihpi_pcm *dpcm = runtime->private_data;
snd_pcm_uframes_t ptr;
u32 samples_played;
u16 err;
if (!snd_pcm_stream_linked(substream)) {
/* NOTE, can use samples played for playback position here and
* in timer fn because it LAGS the actual read pointer, and is a
* better representation of actual playout position
*/
err = hpi_outstream_get_info_ex(ss, dpcm->h_stream, NULL,
NULL, NULL,
&samples_played, NULL);
hpi_handle_error(err);
dpcm->pcm_buf_pos = frames_to_bytes(runtime, samples_played);
}
/* else must return most conservative value found in timer func
* by looping over all streams
*/
ptr = bytes_to_frames(runtime, dpcm->pcm_buf_pos % dpcm->pcm_size);
VPRINTK2("playback_pointer=%04ld\n", (unsigned long)ptr);
return ptr;
}
static void snd_card_asihpi_playback_format(struct snd_card_asihpi *asihpi,
u32 h_stream,
struct snd_pcm_hardware *pcmhw)
{
struct hpi_format hpi_format;
u16 format;
u16 err;
u32 h_control;
u32 sample_rate = 48000;
/* on cards without SRC, must query at valid rate,
* maybe set by external sync
*/
err = hpi_mixer_get_control(ss, asihpi->h_mixer,
HPI_SOURCENODE_CLOCK_SOURCE, 0, 0, 0,
HPI_CONTROL_SAMPLECLOCK, &h_control);
if (!err)
err = hpi_sample_clock_get_sample_rate(ss, h_control,
&sample_rate);
for (format = HPI_FORMAT_PCM8_UNSIGNED;
format <= HPI_FORMAT_PCM24_SIGNED; format++) {
err = hpi_format_create(&hpi_format,
2, format, sample_rate, 128000, 0);
if (!err)
err = hpi_outstream_query_format(ss, h_stream,
&hpi_format);
if (!err && (hpi_to_alsa_formats[format] != -1))
pcmhw->formats |=
(1ULL << hpi_to_alsa_formats[format]);
}
}
static struct snd_pcm_hardware snd_card_asihpi_playback = {
.channels_min = 1,
.channels_max = 2,
.buffer_bytes_max = BUFFER_BYTES_MAX,
.period_bytes_min = PERIOD_BYTES_MIN,
.period_bytes_max = BUFFER_BYTES_MAX / PERIODS_MIN,
.periods_min = PERIODS_MIN,
.periods_max = BUFFER_BYTES_MAX / PERIOD_BYTES_MIN,
.fifo_size = 0,
};
static int snd_card_asihpi_playback_open(struct snd_pcm_substream *substream)
{
struct snd_pcm_runtime *runtime = substream->runtime;
struct snd_card_asihpi_pcm *dpcm;
struct snd_card_asihpi *card = snd_pcm_substream_chip(substream);
int err;
dpcm = kzalloc(sizeof(*dpcm), GFP_KERNEL);
if (dpcm == NULL)
return -ENOMEM;
err =
hpi_outstream_open(ss, card->adapter_index,
substream->number, &dpcm->h_stream);
hpi_handle_error(err);
if (err)
kfree(dpcm);
if (err == HPI_ERROR_OBJ_ALREADY_OPEN)
return -EBUSY;
if (err)
return -EIO;
/*? also check ASI5000 samplerate source
If external, only support external rate.
If internal and other stream playing, cant switch
*/
init_timer(&dpcm->timer);
dpcm->timer.data = (unsigned long) dpcm;
dpcm->timer.function = snd_card_asihpi_timer_function;
dpcm->substream = substream;
runtime->private_data = dpcm;
runtime->private_free = snd_card_asihpi_runtime_free;
snd_card_asihpi_playback.channels_max = card->out_max_chans;
/*?snd_card_asihpi_playback.period_bytes_min =
card->out_max_chans * 4096; */
snd_card_asihpi_playback_format(card, dpcm->h_stream,
&snd_card_asihpi_playback);
snd_card_asihpi_pcm_samplerates(card, &snd_card_asihpi_playback);
snd_card_asihpi_playback.info = SNDRV_PCM_INFO_INTERLEAVED |
SNDRV_PCM_INFO_DOUBLE |
SNDRV_PCM_INFO_BATCH |
SNDRV_PCM_INFO_BLOCK_TRANSFER |
SNDRV_PCM_INFO_PAUSE;
if (card->support_mmap)
snd_card_asihpi_playback.info |= SNDRV_PCM_INFO_MMAP |
SNDRV_PCM_INFO_MMAP_VALID;
if (card->support_grouping)
snd_card_asihpi_playback.info |= SNDRV_PCM_INFO_SYNC_START;
/* struct is copied, so can create initializer dynamically */
runtime->hw = snd_card_asihpi_playback;
if (card->support_mmap)
err = snd_pcm_hw_constraint_pow2(runtime, 0,
SNDRV_PCM_HW_PARAM_BUFFER_BYTES);
if (err < 0)
return err;
snd_pcm_hw_constraint_step(runtime, 0, SNDRV_PCM_HW_PARAM_PERIOD_SIZE,
card->update_interval_frames);
snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_PERIOD_SIZE,
card->update_interval_frames * 4, UINT_MAX);
snd_pcm_set_sync(substream);
snd_printd(KERN_INFO "playback open\n");
return 0;
}
static int snd_card_asihpi_playback_close(struct snd_pcm_substream *substream)
{
struct snd_pcm_runtime *runtime = substream->runtime;
struct snd_card_asihpi_pcm *dpcm = runtime->private_data;
hpi_handle_error(hpi_outstream_close(ss, dpcm->h_stream));
snd_printd(KERN_INFO "playback close\n");
return 0;
}
static int snd_card_asihpi_playback_copy(struct snd_pcm_substream *substream,
int channel,
snd_pcm_uframes_t pos,
void __user *src,
snd_pcm_uframes_t count)
{
struct snd_pcm_runtime *runtime = substream->runtime;
struct snd_card_asihpi_pcm *dpcm = runtime->private_data;
unsigned int len;
len = frames_to_bytes(runtime, count);
if (copy_from_user(runtime->dma_area, src, len))
return -EFAULT;
VPRINTK2(KERN_DEBUG "playback copy%d %u bytes\n",
substream->number, len);
hpi_handle_error(hpi_outstream_write_buf(ss, dpcm->h_stream,
runtime->dma_area, len, &dpcm->format));
return 0;
}
static int snd_card_asihpi_playback_silence(struct snd_pcm_substream *
substream, int channel,
snd_pcm_uframes_t pos,
snd_pcm_uframes_t count)
{
unsigned int len;
struct snd_pcm_runtime *runtime = substream->runtime;
struct snd_card_asihpi_pcm *dpcm = runtime->private_data;
len = frames_to_bytes(runtime, count);
snd_printd(KERN_INFO "playback silence %u bytes\n", len);
memset(runtime->dma_area, 0, len);
hpi_handle_error(hpi_outstream_write_buf(ss, dpcm->h_stream,
runtime->dma_area, len, &dpcm->format));
return 0;
}
static struct snd_pcm_ops snd_card_asihpi_playback_ops = {
.open = snd_card_asihpi_playback_open,
.close = snd_card_asihpi_playback_close,
.ioctl = snd_card_asihpi_playback_ioctl,
.hw_params = snd_card_asihpi_pcm_hw_params,
.hw_free = snd_card_asihpi_hw_free,
.prepare = snd_card_asihpi_playback_prepare,
.trigger = snd_card_asihpi_trigger,
.pointer = snd_card_asihpi_playback_pointer,
.copy = snd_card_asihpi_playback_copy,
.silence = snd_card_asihpi_playback_silence,
};
static struct snd_pcm_ops snd_card_asihpi_playback_mmap_ops = {
.open = snd_card_asihpi_playback_open,
.close = snd_card_asihpi_playback_close,
.ioctl = snd_card_asihpi_playback_ioctl,
.hw_params = snd_card_asihpi_pcm_hw_params,
.hw_free = snd_card_asihpi_hw_free,
.prepare = snd_card_asihpi_playback_prepare,
.trigger = snd_card_asihpi_trigger,
.pointer = snd_card_asihpi_playback_pointer,
};
/***************************** CAPTURE OPS ****************/
static snd_pcm_uframes_t
snd_card_asihpi_capture_pointer(struct snd_pcm_substream *substream)
{
struct snd_pcm_runtime *runtime = substream->runtime;
struct snd_card_asihpi_pcm *dpcm = runtime->private_data;
VPRINTK2("capture pointer %d=%d\n",
substream->number, dpcm->pcm_buf_pos);
/* NOTE Unlike playback can't use actual dwSamplesPlayed
for the capture position, because those samples aren't yet in
the local buffer available for reading.
*/
return bytes_to_frames(runtime, dpcm->pcm_buf_pos % dpcm->pcm_size);
}
static int snd_card_asihpi_capture_ioctl(struct snd_pcm_substream *substream,
unsigned int cmd, void *arg)
{
return snd_pcm_lib_ioctl(substream, cmd, arg);
}
static int snd_card_asihpi_capture_prepare(struct snd_pcm_substream *substream)
{
struct snd_pcm_runtime *runtime = substream->runtime;
struct snd_card_asihpi_pcm *dpcm = runtime->private_data;
hpi_handle_error(hpi_instream_reset(ss, dpcm->h_stream));
dpcm->pcm_irq_pos = 0;
dpcm->pcm_buf_pos = 0;
snd_printd("capture prepare %d\n", substream->number);
return 0;
}
static void snd_card_asihpi_capture_format(struct snd_card_asihpi *asihpi,
u32 h_stream,
struct snd_pcm_hardware *pcmhw)
{
struct hpi_format hpi_format;
u16 format;
u16 err;
u32 h_control;
u32 sample_rate = 48000;
/* on cards without SRC, must query at valid rate,
maybe set by external sync */
err = hpi_mixer_get_control(ss, asihpi->h_mixer,
HPI_SOURCENODE_CLOCK_SOURCE, 0, 0, 0,
HPI_CONTROL_SAMPLECLOCK, &h_control);
if (!err)
err = hpi_sample_clock_get_sample_rate(ss, h_control,
&sample_rate);
for (format = HPI_FORMAT_PCM8_UNSIGNED;
format <= HPI_FORMAT_PCM24_SIGNED; format++) {
err = hpi_format_create(&hpi_format, 2, format,
sample_rate, 128000, 0);
if (!err)
err = hpi_instream_query_format(ss, h_stream,
&hpi_format);
if (!err)
pcmhw->formats |=
(1ULL << hpi_to_alsa_formats[format]);
}
}
static struct snd_pcm_hardware snd_card_asihpi_capture = {
.channels_min = 1,
.channels_max = 2,
.buffer_bytes_max = BUFFER_BYTES_MAX,
.period_bytes_min = PERIOD_BYTES_MIN,
.period_bytes_max = BUFFER_BYTES_MAX / PERIODS_MIN,
.periods_min = PERIODS_MIN,
.periods_max = BUFFER_BYTES_MAX / PERIOD_BYTES_MIN,
.fifo_size = 0,
};
static int snd_card_asihpi_capture_open(struct snd_pcm_substream *substream)
{
struct snd_pcm_runtime *runtime = substream->runtime;
struct snd_card_asihpi *card = snd_pcm_substream_chip(substream);
struct snd_card_asihpi_pcm *dpcm;
int err;
dpcm = kzalloc(sizeof(*dpcm), GFP_KERNEL);
if (dpcm == NULL)
return -ENOMEM;
snd_printd("hpi_instream_open adapter %d stream %d\n",
card->adapter_index, substream->number);
err = hpi_handle_error(
hpi_instream_open(ss, card->adapter_index,
substream->number, &dpcm->h_stream));
if (err)
kfree(dpcm);
if (err == HPI_ERROR_OBJ_ALREADY_OPEN)
return -EBUSY;
if (err)
return -EIO;
init_timer(&dpcm->timer);
dpcm->timer.data = (unsigned long) dpcm;
dpcm->timer.function = snd_card_asihpi_timer_function;
dpcm->substream = substream;
runtime->private_data = dpcm;
runtime->private_free = snd_card_asihpi_runtime_free;
snd_card_asihpi_capture.channels_max = card->in_max_chans;
snd_card_asihpi_capture_format(card, dpcm->h_stream,
&snd_card_asihpi_capture);
snd_card_asihpi_pcm_samplerates(card, &snd_card_asihpi_capture);
snd_card_asihpi_capture.info = SNDRV_PCM_INFO_INTERLEAVED;
if (card->support_mmap)
snd_card_asihpi_capture.info |= SNDRV_PCM_INFO_MMAP |
SNDRV_PCM_INFO_MMAP_VALID;
runtime->hw = snd_card_asihpi_capture;
if (card->support_mmap)
err = snd_pcm_hw_constraint_pow2(runtime, 0,
SNDRV_PCM_HW_PARAM_BUFFER_BYTES);
if (err < 0)
return err;
snd_pcm_hw_constraint_step(runtime, 0, SNDRV_PCM_HW_PARAM_PERIOD_SIZE,
card->update_interval_frames);
snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_PERIOD_SIZE,
card->update_interval_frames * 2, UINT_MAX);
snd_pcm_set_sync(substream);
return 0;
}
static int snd_card_asihpi_capture_close(struct snd_pcm_substream *substream)
{
struct snd_card_asihpi_pcm *dpcm = substream->runtime->private_data;
hpi_handle_error(hpi_instream_close(ss, dpcm->h_stream));
return 0;
}
static int snd_card_asihpi_capture_copy(struct snd_pcm_substream *substream,
int channel, snd_pcm_uframes_t pos,
void __user *dst, snd_pcm_uframes_t count)
{
struct snd_pcm_runtime *runtime = substream->runtime;
struct snd_card_asihpi_pcm *dpcm = runtime->private_data;
u32 data_size;
data_size = frames_to_bytes(runtime, count);
VPRINTK2("capture copy%d %d bytes\n", substream->number, data_size);
hpi_handle_error(hpi_instream_read_buf(ss, dpcm->h_stream,
runtime->dma_area, data_size));
/* Used by capture_pointer */
dpcm->pcm_irq_pos = dpcm->pcm_irq_pos + data_size;
if (copy_to_user(dst, runtime->dma_area, data_size))
return -EFAULT;
return 0;
}
static struct snd_pcm_ops snd_card_asihpi_capture_mmap_ops = {
.open = snd_card_asihpi_capture_open,
.close = snd_card_asihpi_capture_close,
.ioctl = snd_card_asihpi_capture_ioctl,
.hw_params = snd_card_asihpi_pcm_hw_params,
.hw_free = snd_card_asihpi_hw_free,
.prepare = snd_card_asihpi_capture_prepare,
.trigger = snd_card_asihpi_trigger,
.pointer = snd_card_asihpi_capture_pointer,
};
static struct snd_pcm_ops snd_card_asihpi_capture_ops = {
.open = snd_card_asihpi_capture_open,
.close = snd_card_asihpi_capture_close,
.ioctl = snd_card_asihpi_capture_ioctl,
.hw_params = snd_card_asihpi_pcm_hw_params,
.hw_free = snd_card_asihpi_hw_free,
.prepare = snd_card_asihpi_capture_prepare,
.trigger = snd_card_asihpi_trigger,
.pointer = snd_card_asihpi_capture_pointer,
.copy = snd_card_asihpi_capture_copy
};
static int __devinit snd_card_asihpi_pcm_new(struct snd_card_asihpi *asihpi,
int device, int substreams)
{
struct snd_pcm *pcm;
int err;
err = snd_pcm_new(asihpi->card, "asihpi PCM", device,
asihpi->num_outstreams, asihpi->num_instreams,
&pcm);
if (err < 0)
return err;
/* pointer to ops struct is stored, dont change ops afterwards! */
if (asihpi->support_mmap) {
snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK,
&snd_card_asihpi_playback_mmap_ops);
snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE,
&snd_card_asihpi_capture_mmap_ops);
} else {
snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK,
&snd_card_asihpi_playback_ops);
snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE,
&snd_card_asihpi_capture_ops);
}
pcm->private_data = asihpi;
pcm->info_flags = 0;
strcpy(pcm->name, "asihpi PCM");
/*? do we want to emulate MMAP for non-BBM cards?
Jack doesn't work with ALSAs MMAP emulation - WHY NOT? */
snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV,
snd_dma_pci_data(asihpi->pci),
64*1024, BUFFER_BYTES_MAX);
return 0;
}
/***************************** MIXER CONTROLS ****************/
struct hpi_control {
u32 h_control;
u16 control_type;
u16 src_node_type;
u16 src_node_index;
u16 dst_node_type;
u16 dst_node_index;
u16 band;
char name[44]; /* copied to snd_ctl_elem_id.name[44]; */
};
static char *asihpi_tuner_band_names[] =
{
"invalid",
"AM",
"FM mono",
"TV NTSC-M",
"FM stereo",
"AUX",
"TV PAL BG",
"TV PAL I",
"TV PAL DK",
"TV SECAM",
};
compile_time_assert(
(ARRAY_SIZE(asihpi_tuner_band_names) ==
(HPI_TUNER_BAND_LAST+1)),
assert_tuner_band_names_size);
#if ASI_STYLE_NAMES
static char *asihpi_src_names[] =
{
"no source",
"outstream",
"line_in",
"aes_in",
"tuner",
"RF",
"clock",
"bitstr",
"mic",
"cobranet",
"analog_in",
"adapter",
};
#else
static char *asihpi_src_names[] =
{
"no source",
"PCM playback",
"line in",
"digital in",
"tuner",
"RF",
"clock",
"bitstream",
"mic",
"cobranet in",
"analog in",
"adapter",
};
#endif
compile_time_assert(
(ARRAY_SIZE(asihpi_src_names) ==
(HPI_SOURCENODE_LAST_INDEX-HPI_SOURCENODE_BASE+1)),
assert_src_names_size);
#if ASI_STYLE_NAMES
static char *asihpi_dst_names[] =
{
"no destination",
"instream",
"line_out",
"aes_out",
"RF",
"speaker" ,
"cobranet",
"analog_out",
};
#else
static char *asihpi_dst_names[] =
{
"no destination",
"PCM capture",
"line out",
"digital out",
"RF",
"speaker",
"cobranet out",
"analog out"
};
#endif
compile_time_assert(
(ARRAY_SIZE(asihpi_dst_names) ==
(HPI_DESTNODE_LAST_INDEX-HPI_DESTNODE_BASE+1)),
assert_dst_names_size);
static inline int ctl_add(struct snd_card *card, struct snd_kcontrol_new *ctl,
struct snd_card_asihpi *asihpi)
{
int err;
err = snd_ctl_add(card, snd_ctl_new1(ctl, asihpi));
if (err < 0)
return err;
else if (mixer_dump)
snd_printk(KERN_INFO "added %s(%d)\n", ctl->name, ctl->index);
return 0;
}
/* Convert HPI control name and location into ALSA control name */
static void asihpi_ctl_init(struct snd_kcontrol_new *snd_control,
struct hpi_control *hpi_ctl,
char *name)
{
memset(snd_control, 0, sizeof(*snd_control));
snd_control->name = hpi_ctl->name;
snd_control->private_value = hpi_ctl->h_control;
snd_control->iface = SNDRV_CTL_ELEM_IFACE_MIXER;
snd_control->index = 0;
if (hpi_ctl->src_node_type && hpi_ctl->dst_node_type)
sprintf(hpi_ctl->name, "%s%d to %s%d %s",
asihpi_src_names[hpi_ctl->src_node_type],
hpi_ctl->src_node_index,
asihpi_dst_names[hpi_ctl->dst_node_type],
hpi_ctl->dst_node_index,
name);
else if (hpi_ctl->dst_node_type) {
sprintf(hpi_ctl->name, "%s%d %s",
asihpi_dst_names[hpi_ctl->dst_node_type],
hpi_ctl->dst_node_index,
name);
} else {
sprintf(hpi_ctl->name, "%s%d %s",
asihpi_src_names[hpi_ctl->src_node_type],
hpi_ctl->src_node_index,
name);
}
}
/*------------------------------------------------------------
Volume controls
------------------------------------------------------------*/
#define VOL_STEP_mB 1
static int snd_asihpi_volume_info(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_info *uinfo)
{
u32 h_control = kcontrol->private_value;
u16 err;
/* native gains are in millibels */
short min_gain_mB;
short max_gain_mB;
short step_gain_mB;
err = hpi_volume_query_range(ss, h_control,
&min_gain_mB, &max_gain_mB, &step_gain_mB);
if (err) {
max_gain_mB = 0;
min_gain_mB = -10000;
step_gain_mB = VOL_STEP_mB;
}
uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
uinfo->count = 2;
uinfo->value.integer.min = min_gain_mB / VOL_STEP_mB;
uinfo->value.integer.max = max_gain_mB / VOL_STEP_mB;
uinfo->value.integer.step = step_gain_mB / VOL_STEP_mB;
return 0;
}
static int snd_asihpi_volume_get(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
u32 h_control = kcontrol->private_value;
short an_gain_mB[HPI_MAX_CHANNELS];
hpi_handle_error(hpi_volume_get_gain(ss, h_control, an_gain_mB));
ucontrol->value.integer.value[0] = an_gain_mB[0] / VOL_STEP_mB;
ucontrol->value.integer.value[1] = an_gain_mB[1] / VOL_STEP_mB;
return 0;
}
static int snd_asihpi_volume_put(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
int change;
u32 h_control = kcontrol->private_value;
short an_gain_mB[HPI_MAX_CHANNELS];
an_gain_mB[0] =
(ucontrol->value.integer.value[0]) * VOL_STEP_mB;
an_gain_mB[1] =
(ucontrol->value.integer.value[1]) * VOL_STEP_mB;
/* change = asihpi->mixer_volume[addr][0] != left ||
asihpi->mixer_volume[addr][1] != right;
*/
change = 1;
hpi_handle_error(hpi_volume_set_gain(ss, h_control, an_gain_mB));
return change;
}
static const DECLARE_TLV_DB_SCALE(db_scale_100, -10000, VOL_STEP_mB, 0);
static int __devinit snd_asihpi_volume_add(struct snd_card_asihpi *asihpi,
struct hpi_control *hpi_ctl)
{
struct snd_card *card = asihpi->card;
struct snd_kcontrol_new snd_control;
asihpi_ctl_init(&snd_control, hpi_ctl, "volume");
snd_control.access = SNDRV_CTL_ELEM_ACCESS_READWRITE |
SNDRV_CTL_ELEM_ACCESS_TLV_READ;
snd_control.info = snd_asihpi_volume_info;
snd_control.get = snd_asihpi_volume_get;
snd_control.put = snd_asihpi_volume_put;
snd_control.tlv.p = db_scale_100;
return ctl_add(card, &snd_control, asihpi);
}
/*------------------------------------------------------------
Level controls
------------------------------------------------------------*/
static int snd_asihpi_level_info(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_info *uinfo)
{
u32 h_control = kcontrol->private_value;
u16 err;
short min_gain_mB;
short max_gain_mB;
short step_gain_mB;
err =
hpi_level_query_range(ss, h_control, &min_gain_mB,
&max_gain_mB, &step_gain_mB);
if (err) {
max_gain_mB = 2400;
min_gain_mB = -1000;
step_gain_mB = 100;
}
uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
uinfo->count = 2;
uinfo->value.integer.min = min_gain_mB / HPI_UNITS_PER_dB;
uinfo->value.integer.max = max_gain_mB / HPI_UNITS_PER_dB;
uinfo->value.integer.step = step_gain_mB / HPI_UNITS_PER_dB;
return 0;
}
static int snd_asihpi_level_get(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
u32 h_control = kcontrol->private_value;
short an_gain_mB[HPI_MAX_CHANNELS];
hpi_handle_error(hpi_level_get_gain(ss, h_control, an_gain_mB));
ucontrol->value.integer.value[0] =
an_gain_mB[0] / HPI_UNITS_PER_dB;
ucontrol->value.integer.value[1] =
an_gain_mB[1] / HPI_UNITS_PER_dB;
return 0;
}
static int snd_asihpi_level_put(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
int change;
u32 h_control = kcontrol->private_value;
short an_gain_mB[HPI_MAX_CHANNELS];
an_gain_mB[0] =
(ucontrol->value.integer.value[0]) * HPI_UNITS_PER_dB;
an_gain_mB[1] =
(ucontrol->value.integer.value[1]) * HPI_UNITS_PER_dB;
/* change = asihpi->mixer_level[addr][0] != left ||
asihpi->mixer_level[addr][1] != right;
*/
change = 1;
hpi_handle_error(hpi_level_set_gain(ss, h_control, an_gain_mB));
return change;
}
static const DECLARE_TLV_DB_SCALE(db_scale_level, -1000, 100, 0);
static int __devinit snd_asihpi_level_add(struct snd_card_asihpi *asihpi,
struct hpi_control *hpi_ctl)
{
struct snd_card *card = asihpi->card;
struct snd_kcontrol_new snd_control;
/* can't use 'volume' cos some nodes have volume as well */
asihpi_ctl_init(&snd_control, hpi_ctl, "level");
snd_control.access = SNDRV_CTL_ELEM_ACCESS_READWRITE |
SNDRV_CTL_ELEM_ACCESS_TLV_READ;
snd_control.info = snd_asihpi_level_info;
snd_control.get = snd_asihpi_level_get;
snd_control.put = snd_asihpi_level_put;
snd_control.tlv.p = db_scale_level;
return ctl_add(card, &snd_control, asihpi);
}
/*------------------------------------------------------------
AESEBU controls
------------------------------------------------------------*/
/* AESEBU format */
static char *asihpi_aesebu_format_names[] =
{
"N/A",
"S/PDIF",
"AES/EBU",
};
static int snd_asihpi_aesebu_format_info(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_info *uinfo)
{
uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
uinfo->count = 1;
uinfo->value.enumerated.items = 3;
if (uinfo->value.enumerated.item >= uinfo->value.enumerated.items)
uinfo->value.enumerated.item =
uinfo->value.enumerated.items - 1;
strcpy(uinfo->value.enumerated.name,
asihpi_aesebu_format_names[uinfo->value.enumerated.item]);
return 0;
}
static int snd_asihpi_aesebu_format_get(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol,
u16 (*func)(const struct hpi_hsubsys *, u32, u16 *))
{
u32 h_control = kcontrol->private_value;
u16 source, err;
err = func(ss, h_control, &source);
/* default to N/A */
ucontrol->value.enumerated.item[0] = 0;
/* return success but set the control to N/A */
if (err)
return 0;
if (source == HPI_AESEBU_FORMAT_SPDIF)
ucontrol->value.enumerated.item[0] = 1;
if (source == HPI_AESEBU_FORMAT_AESEBU)
ucontrol->value.enumerated.item[0] = 2;
return 0;
}
static int snd_asihpi_aesebu_format_put(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol,
u16 (*func)(const struct hpi_hsubsys *, u32, u16))
{
u32 h_control = kcontrol->private_value;
/* default to S/PDIF */
u16 source = HPI_AESEBU_FORMAT_SPDIF;
if (ucontrol->value.enumerated.item[0] == 1)
source = HPI_AESEBU_FORMAT_SPDIF;
if (ucontrol->value.enumerated.item[0] == 2)
source = HPI_AESEBU_FORMAT_AESEBU;
if (func(ss, h_control, source) != 0)
return -EINVAL;
return 1;
}
static int snd_asihpi_aesebu_rx_format_get(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol) {
return snd_asihpi_aesebu_format_get(kcontrol, ucontrol,
HPI_AESEBU__receiver_get_format);
}
static int snd_asihpi_aesebu_rx_format_put(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol) {
return snd_asihpi_aesebu_format_put(kcontrol, ucontrol,
HPI_AESEBU__receiver_set_format);
}
static int snd_asihpi_aesebu_rxstatus_info(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_info *uinfo)
{
uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
uinfo->count = 1;
uinfo->value.integer.min = 0;
uinfo->value.integer.max = 0X1F;
uinfo->value.integer.step = 1;
return 0;
}
static int snd_asihpi_aesebu_rxstatus_get(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol) {
u32 h_control = kcontrol->private_value;
u16 status;
hpi_handle_error(HPI_AESEBU__receiver_get_error_status(
ss, h_control, &status));
ucontrol->value.integer.value[0] = status;
return 0;
}
static int __devinit snd_asihpi_aesebu_rx_add(struct snd_card_asihpi *asihpi,
struct hpi_control *hpi_ctl)
{
struct snd_card *card = asihpi->card;
struct snd_kcontrol_new snd_control;
asihpi_ctl_init(&snd_control, hpi_ctl, "format");
snd_control.access = SNDRV_CTL_ELEM_ACCESS_READWRITE;
snd_control.info = snd_asihpi_aesebu_format_info;
snd_control.get = snd_asihpi_aesebu_rx_format_get;
snd_control.put = snd_asihpi_aesebu_rx_format_put;
if (ctl_add(card, &snd_control, asihpi) < 0)
return -EINVAL;
asihpi_ctl_init(&snd_control, hpi_ctl, "status");
snd_control.access =
SNDRV_CTL_ELEM_ACCESS_VOLATILE | SNDRV_CTL_ELEM_ACCESS_READ;
snd_control.info = snd_asihpi_aesebu_rxstatus_info;
snd_control.get = snd_asihpi_aesebu_rxstatus_get;
return ctl_add(card, &snd_control, asihpi);
}
static int snd_asihpi_aesebu_tx_format_get(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol) {
return snd_asihpi_aesebu_format_get(kcontrol, ucontrol,
HPI_AESEBU__transmitter_get_format);
}
static int snd_asihpi_aesebu_tx_format_put(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol) {
return snd_asihpi_aesebu_format_put(kcontrol, ucontrol,
HPI_AESEBU__transmitter_set_format);
}
static int __devinit snd_asihpi_aesebu_tx_add(struct snd_card_asihpi *asihpi,
struct hpi_control *hpi_ctl)
{
struct snd_card *card = asihpi->card;
struct snd_kcontrol_new snd_control;
asihpi_ctl_init(&snd_control, hpi_ctl, "format");
snd_control.access = SNDRV_CTL_ELEM_ACCESS_READWRITE;
snd_control.info = snd_asihpi_aesebu_format_info;
snd_control.get = snd_asihpi_aesebu_tx_format_get;
snd_control.put = snd_asihpi_aesebu_tx_format_put;
return ctl_add(card, &snd_control, asihpi);
}
/*------------------------------------------------------------
Tuner controls
------------------------------------------------------------*/
/* Gain */
static int snd_asihpi_tuner_gain_info(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_info *uinfo)
{
u32 h_control = kcontrol->private_value;
u16 err;
short idx;
u16 gain_range[3];
for (idx = 0; idx < 3; idx++) {
err = hpi_tuner_query_gain(ss, h_control,
idx, &gain_range[idx]);
if (err != 0)
return err;
}
uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
uinfo->count = 1;
uinfo->value.integer.min = ((int)gain_range[0]) / HPI_UNITS_PER_dB;
uinfo->value.integer.max = ((int)gain_range[1]) / HPI_UNITS_PER_dB;
uinfo->value.integer.step = ((int) gain_range[2]) / HPI_UNITS_PER_dB;
return 0;
}
static int snd_asihpi_tuner_gain_get(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
/*
struct snd_card_asihpi *asihpi = snd_kcontrol_chip(kcontrol);
*/
u32 h_control = kcontrol->private_value;
short gain;
hpi_handle_error(hpi_tuner_get_gain(ss, h_control, &gain));
ucontrol->value.integer.value[0] = gain / HPI_UNITS_PER_dB;
return 0;
}
static int snd_asihpi_tuner_gain_put(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
/*
struct snd_card_asihpi *asihpi = snd_kcontrol_chip(kcontrol);
*/
u32 h_control = kcontrol->private_value;
short gain;
gain = (ucontrol->value.integer.value[0]) * HPI_UNITS_PER_dB;
hpi_handle_error(hpi_tuner_set_gain(ss, h_control, gain));
return 1;
}
/* Band */
static int asihpi_tuner_band_query(struct snd_kcontrol *kcontrol,
u16 *band_list, u32 len) {
u32 h_control = kcontrol->private_value;
u16 err = 0;
u32 i;
for (i = 0; i < len; i++) {
err = hpi_tuner_query_band(ss,
h_control, i, &band_list[i]);
if (err != 0)
break;
}
if (err && (err != HPI_ERROR_INVALID_OBJ_INDEX))
return -EIO;
return i;
}
static int snd_asihpi_tuner_band_info(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_info *uinfo)
{
u16 tuner_bands[HPI_TUNER_BAND_LAST];
int num_bands = 0;
num_bands = asihpi_tuner_band_query(kcontrol, tuner_bands,
HPI_TUNER_BAND_LAST);
if (num_bands < 0)
return num_bands;
uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
uinfo->count = 1;
uinfo->value.enumerated.items = num_bands;
if (num_bands > 0) {
if (uinfo->value.enumerated.item >=
uinfo->value.enumerated.items)
uinfo->value.enumerated.item =
uinfo->value.enumerated.items - 1;
strcpy(uinfo->value.enumerated.name,
asihpi_tuner_band_names[
tuner_bands[uinfo->value.enumerated.item]]);
}
return 0;
}
static int snd_asihpi_tuner_band_get(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
u32 h_control = kcontrol->private_value;
/*
struct snd_card_asihpi *asihpi = snd_kcontrol_chip(kcontrol);
*/
u16 band, idx;
u16 tuner_bands[HPI_TUNER_BAND_LAST];
u32 num_bands = 0;
num_bands = asihpi_tuner_band_query(kcontrol, tuner_bands,
HPI_TUNER_BAND_LAST);
hpi_handle_error(hpi_tuner_get_band(ss, h_control, &band));
ucontrol->value.enumerated.item[0] = -1;
for (idx = 0; idx < HPI_TUNER_BAND_LAST; idx++)
if (tuner_bands[idx] == band) {
ucontrol->value.enumerated.item[0] = idx;
break;
}
return 0;
}
static int snd_asihpi_tuner_band_put(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
/*
struct snd_card_asihpi *asihpi = snd_kcontrol_chip(kcontrol);
*/
u32 h_control = kcontrol->private_value;
u16 band;
u16 tuner_bands[HPI_TUNER_BAND_LAST];
u32 num_bands = 0;
num_bands = asihpi_tuner_band_query(kcontrol, tuner_bands,
HPI_TUNER_BAND_LAST);
band = tuner_bands[ucontrol->value.enumerated.item[0]];
hpi_handle_error(hpi_tuner_set_band(ss, h_control, band));
return 1;
}
/* Freq */
static int snd_asihpi_tuner_freq_info(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_info *uinfo)
{
u32 h_control = kcontrol->private_value;
u16 err;
u16 tuner_bands[HPI_TUNER_BAND_LAST];
u16 num_bands = 0, band_iter, idx;
u32 freq_range[3], temp_freq_range[3];
num_bands = asihpi_tuner_band_query(kcontrol, tuner_bands,
HPI_TUNER_BAND_LAST);
freq_range[0] = INT_MAX;
freq_range[1] = 0;
freq_range[2] = INT_MAX;
for (band_iter = 0; band_iter < num_bands; band_iter++) {
for (idx = 0; idx < 3; idx++) {
err = hpi_tuner_query_frequency(ss, h_control,
idx, tuner_bands[band_iter],
&temp_freq_range[idx]);
if (err != 0)
return err;
}
/* skip band with bogus stepping */
if (temp_freq_range[2] <= 0)
continue;
if (temp_freq_range[0] < freq_range[0])
freq_range[0] = temp_freq_range[0];
if (temp_freq_range[1] > freq_range[1])
freq_range[1] = temp_freq_range[1];
if (temp_freq_range[2] < freq_range[2])
freq_range[2] = temp_freq_range[2];
}
uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
uinfo->count = 1;
uinfo->value.integer.min = ((int)freq_range[0]);
uinfo->value.integer.max = ((int)freq_range[1]);
uinfo->value.integer.step = ((int)freq_range[2]);
return 0;
}
static int snd_asihpi_tuner_freq_get(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
u32 h_control = kcontrol->private_value;
u32 freq;
hpi_handle_error(hpi_tuner_get_frequency(ss, h_control, &freq));
ucontrol->value.integer.value[0] = freq;
return 0;
}
static int snd_asihpi_tuner_freq_put(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
u32 h_control = kcontrol->private_value;
u32 freq;
freq = ucontrol->value.integer.value[0];
hpi_handle_error(hpi_tuner_set_frequency(ss, h_control, freq));
return 1;
}
/* Tuner control group initializer */
static int __devinit snd_asihpi_tuner_add(struct snd_card_asihpi *asihpi,
struct hpi_control *hpi_ctl)
{
struct snd_card *card = asihpi->card;
struct snd_kcontrol_new snd_control;
snd_control.private_value = hpi_ctl->h_control;
snd_control.access = SNDRV_CTL_ELEM_ACCESS_READWRITE;
if (!hpi_tuner_get_gain(ss, hpi_ctl->h_control, NULL)) {
asihpi_ctl_init(&snd_control, hpi_ctl, "gain");
snd_control.info = snd_asihpi_tuner_gain_info;
snd_control.get = snd_asihpi_tuner_gain_get;
snd_control.put = snd_asihpi_tuner_gain_put;
if (ctl_add(card, &snd_control, asihpi) < 0)
return -EINVAL;
}
asihpi_ctl_init(&snd_control, hpi_ctl, "band");
snd_control.info = snd_asihpi_tuner_band_info;
snd_control.get = snd_asihpi_tuner_band_get;
snd_control.put = snd_asihpi_tuner_band_put;
if (ctl_add(card, &snd_control, asihpi) < 0)
return -EINVAL;
asihpi_ctl_init(&snd_control, hpi_ctl, "freq");
snd_control.info = snd_asihpi_tuner_freq_info;
snd_control.get = snd_asihpi_tuner_freq_get;
snd_control.put = snd_asihpi_tuner_freq_put;
return ctl_add(card, &snd_control, asihpi);
}
/*------------------------------------------------------------
Meter controls
------------------------------------------------------------*/
static int snd_asihpi_meter_info(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_info *uinfo)
{
uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
uinfo->count = HPI_MAX_CHANNELS;
uinfo->value.integer.min = 0;
uinfo->value.integer.max = 0x7FFFFFFF;
return 0;
}
/* linear values for 10dB steps */
static int log2lin[] = {
0x7FFFFFFF, /* 0dB */
679093956,
214748365,
67909396,
21474837,
6790940,
2147484, /* -60dB */
679094,
214748, /* -80 */
67909,
21475, /* -100 */
6791,
2147,
679,
214,
68,
21,
7,
2
};
static int snd_asihpi_meter_get(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
u32 h_control = kcontrol->private_value;
short an_gain_mB[HPI_MAX_CHANNELS], i;
u16 err;
err = hpi_meter_get_peak(ss, h_control, an_gain_mB);
for (i = 0; i < HPI_MAX_CHANNELS; i++) {
if (err) {
ucontrol->value.integer.value[i] = 0;
} else if (an_gain_mB[i] >= 0) {
ucontrol->value.integer.value[i] =
an_gain_mB[i] << 16;
} else {
/* -ve is log value in millibels < -60dB,
* convert to (roughly!) linear,
*/
ucontrol->value.integer.value[i] =
log2lin[an_gain_mB[i] / -1000];
}
}
return 0;
}
static int __devinit snd_asihpi_meter_add(struct snd_card_asihpi *asihpi,
struct hpi_control *hpi_ctl, int subidx)
{
struct snd_card *card = asihpi->card;
struct snd_kcontrol_new snd_control;
asihpi_ctl_init(&snd_control, hpi_ctl, "meter");
snd_control.access =
SNDRV_CTL_ELEM_ACCESS_VOLATILE | SNDRV_CTL_ELEM_ACCESS_READ;
snd_control.info = snd_asihpi_meter_info;
snd_control.get = snd_asihpi_meter_get;
snd_control.index = subidx;
return ctl_add(card, &snd_control, asihpi);
}
/*------------------------------------------------------------
Multiplexer controls
------------------------------------------------------------*/
static int snd_card_asihpi_mux_count_sources(struct snd_kcontrol *snd_control)
{
u32 h_control = snd_control->private_value;
struct hpi_control hpi_ctl;
int s, err;
for (s = 0; s < 32; s++) {
err = hpi_multiplexer_query_source(ss, h_control, s,
&hpi_ctl.
src_node_type,
&hpi_ctl.
src_node_index);
if (err)
break;
}
return s;
}
static int snd_asihpi_mux_info(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_info *uinfo)
{
int err;
u16 src_node_type, src_node_index;
u32 h_control = kcontrol->private_value;
uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
uinfo->count = 1;
uinfo->value.enumerated.items =
snd_card_asihpi_mux_count_sources(kcontrol);
if (uinfo->value.enumerated.item >= uinfo->value.enumerated.items)
uinfo->value.enumerated.item =
uinfo->value.enumerated.items - 1;
err =
hpi_multiplexer_query_source(ss, h_control,
uinfo->value.enumerated.item,
&src_node_type, &src_node_index);
sprintf(uinfo->value.enumerated.name, "%s %d",
asihpi_src_names[src_node_type - HPI_SOURCENODE_BASE],
src_node_index);
return 0;
}
static int snd_asihpi_mux_get(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
u32 h_control = kcontrol->private_value;
u16 source_type, source_index;
u16 src_node_type, src_node_index;
int s;
hpi_handle_error(hpi_multiplexer_get_source(ss, h_control,
&source_type, &source_index));
/* Should cache this search result! */
for (s = 0; s < 256; s++) {
if (hpi_multiplexer_query_source(ss, h_control, s,
&src_node_type, &src_node_index))
break;
if ((source_type == src_node_type)
&& (source_index == src_node_index)) {
ucontrol->value.enumerated.item[0] = s;
return 0;
}
}
snd_printd(KERN_WARNING
"control %x failed to match mux source %hu %hu\n",
h_control, source_type, source_index);
ucontrol->value.enumerated.item[0] = 0;
return 0;
}
static int snd_asihpi_mux_put(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
int change;
u32 h_control = kcontrol->private_value;
u16 source_type, source_index;
u16 e;
change = 1;
e = hpi_multiplexer_query_source(ss, h_control,
ucontrol->value.enumerated.item[0],
&source_type, &source_index);
if (!e)
hpi_handle_error(
hpi_multiplexer_set_source(ss, h_control,
source_type, source_index));
return change;
}
static int __devinit snd_asihpi_mux_add(struct snd_card_asihpi *asihpi,
struct hpi_control *hpi_ctl)
{
struct snd_card *card = asihpi->card;
struct snd_kcontrol_new snd_control;
#if ASI_STYLE_NAMES
asihpi_ctl_init(&snd_control, hpi_ctl, "multiplexer");
#else
asihpi_ctl_init(&snd_control, hpi_ctl, "route");
#endif
snd_control.access = SNDRV_CTL_ELEM_ACCESS_READWRITE;
snd_control.info = snd_asihpi_mux_info;
snd_control.get = snd_asihpi_mux_get;
snd_control.put = snd_asihpi_mux_put;
return ctl_add(card, &snd_control, asihpi);
}
/*------------------------------------------------------------
Channel mode controls
------------------------------------------------------------*/
static int snd_asihpi_cmode_info(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_info *uinfo)
{
static char *mode_names[HPI_CHANNEL_MODE_LAST] = {
"normal", "swap",
"from_left", "from_right",
"to_left", "to_right"
};
u32 h_control = kcontrol->private_value;
u16 mode;
int i;
/* HPI channel mode values can be from 1 to 6
Some adapters only support a contiguous subset
*/
for (i = 0; i < HPI_CHANNEL_MODE_LAST; i++)
if (hpi_channel_mode_query_mode(
ss, h_control, i, &mode))
break;
uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
uinfo->count = 1;
uinfo->value.enumerated.items = i;
if (uinfo->value.enumerated.item >= i)
uinfo->value.enumerated.item = i - 1;
strcpy(uinfo->value.enumerated.name,
mode_names[uinfo->value.enumerated.item]);
return 0;
}
static int snd_asihpi_cmode_get(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
u32 h_control = kcontrol->private_value;
u16 mode;
if (hpi_channel_mode_get(ss, h_control, &mode))
mode = 1;
ucontrol->value.enumerated.item[0] = mode - 1;
return 0;
}
static int snd_asihpi_cmode_put(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
int change;
u32 h_control = kcontrol->private_value;
change = 1;
hpi_handle_error(hpi_channel_mode_set(ss, h_control,
ucontrol->value.enumerated.item[0] + 1));
return change;
}
static int __devinit snd_asihpi_cmode_add(struct snd_card_asihpi *asihpi,
struct hpi_control *hpi_ctl)
{
struct snd_card *card = asihpi->card;
struct snd_kcontrol_new snd_control;
asihpi_ctl_init(&snd_control, hpi_ctl, "channel mode");
snd_control.access = SNDRV_CTL_ELEM_ACCESS_READWRITE;
snd_control.info = snd_asihpi_cmode_info;
snd_control.get = snd_asihpi_cmode_get;
snd_control.put = snd_asihpi_cmode_put;
return ctl_add(card, &snd_control, asihpi);
}
/*------------------------------------------------------------
Sampleclock source controls
------------------------------------------------------------*/
static char *sampleclock_sources[MAX_CLOCKSOURCES] =
{ "N/A", "local PLL", "AES/EBU sync", "word external", "word header",
"SMPTE", "AES/EBU in1", "auto", "network", "invalid",
"prev module",
"AES/EBU in2", "AES/EBU in3", "AES/EBU in4", "AES/EBU in5",
"AES/EBU in6", "AES/EBU in7", "AES/EBU in8"};
static int snd_asihpi_clksrc_info(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_info *uinfo)
{
struct snd_card_asihpi *asihpi =
(struct snd_card_asihpi *)(kcontrol->private_data);
struct clk_cache *clkcache = &asihpi->cc;
uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
uinfo->count = 1;
uinfo->value.enumerated.items = clkcache->count;
if (uinfo->value.enumerated.item >= uinfo->value.enumerated.items)
uinfo->value.enumerated.item =
uinfo->value.enumerated.items - 1;
strcpy(uinfo->value.enumerated.name,
clkcache->s[uinfo->value.enumerated.item].name);
return 0;
}
static int snd_asihpi_clksrc_get(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_card_asihpi *asihpi =
(struct snd_card_asihpi *)(kcontrol->private_data);
struct clk_cache *clkcache = &asihpi->cc;
u32 h_control = kcontrol->private_value;
u16 source, srcindex = 0;
int i;
ucontrol->value.enumerated.item[0] = 0;
if (hpi_sample_clock_get_source(ss, h_control, &source))
source = 0;
if (source == HPI_SAMPLECLOCK_SOURCE_AESEBU_INPUT)
if (hpi_sample_clock_get_source_index(ss, h_control, &srcindex))
srcindex = 0;
for (i = 0; i < clkcache->count; i++)
if ((clkcache->s[i].source == source) &&
(clkcache->s[i].index == srcindex))
break;
ucontrol->value.enumerated.item[0] = i;
return 0;
}
static int snd_asihpi_clksrc_put(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_card_asihpi *asihpi =
(struct snd_card_asihpi *)(kcontrol->private_data);
struct clk_cache *clkcache = &asihpi->cc;
int change, item;
u32 h_control = kcontrol->private_value;
change = 1;
item = ucontrol->value.enumerated.item[0];
if (item >= clkcache->count)
item = clkcache->count-1;
hpi_handle_error(hpi_sample_clock_set_source(ss,
h_control, clkcache->s[item].source));
if (clkcache->s[item].source == HPI_SAMPLECLOCK_SOURCE_AESEBU_INPUT)
hpi_handle_error(hpi_sample_clock_set_source_index(ss,
h_control, clkcache->s[item].index));
return change;
}
/*------------------------------------------------------------
Clkrate controls
------------------------------------------------------------*/
/* Need to change this to enumerated control with list of rates */
static int snd_asihpi_clklocal_info(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_info *uinfo)
{
uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
uinfo->count = 1;
uinfo->value.integer.min = 8000;
uinfo->value.integer.max = 192000;
uinfo->value.integer.step = 100;
return 0;
}
static int snd_asihpi_clklocal_get(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
u32 h_control = kcontrol->private_value;
u32 rate;
u16 e;
e = hpi_sample_clock_get_local_rate(ss, h_control, &rate);
if (!e)
ucontrol->value.integer.value[0] = rate;
else
ucontrol->value.integer.value[0] = 0;
return 0;
}
static int snd_asihpi_clklocal_put(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
int change;
u32 h_control = kcontrol->private_value;
/* change = asihpi->mixer_clkrate[addr][0] != left ||
asihpi->mixer_clkrate[addr][1] != right;
*/
change = 1;
hpi_handle_error(hpi_sample_clock_set_local_rate(ss, h_control,
ucontrol->value.integer.value[0]));
return change;
}
static int snd_asihpi_clkrate_info(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_info *uinfo)
{
uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
uinfo->count = 1;
uinfo->value.integer.min = 8000;
uinfo->value.integer.max = 192000;
uinfo->value.integer.step = 100;
return 0;
}
static int snd_asihpi_clkrate_get(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
u32 h_control = kcontrol->private_value;
u32 rate;
u16 e;
e = hpi_sample_clock_get_sample_rate(ss, h_control, &rate);
if (!e)
ucontrol->value.integer.value[0] = rate;
else
ucontrol->value.integer.value[0] = 0;
return 0;
}
static int __devinit snd_asihpi_sampleclock_add(struct snd_card_asihpi *asihpi,
struct hpi_control *hpi_ctl)
{
struct snd_card *card = asihpi->card;
struct snd_kcontrol_new snd_control;
struct clk_cache *clkcache = &asihpi->cc;
u32 hSC = hpi_ctl->h_control;
int has_aes_in = 0;
int i, j;
u16 source;
snd_control.private_value = hpi_ctl->h_control;
clkcache->has_local = 0;
for (i = 0; i <= HPI_SAMPLECLOCK_SOURCE_LAST; i++) {
if (hpi_sample_clock_query_source(ss, hSC,
i, &source))
break;
clkcache->s[i].source = source;
clkcache->s[i].index = 0;
clkcache->s[i].name = sampleclock_sources[source];
if (source == HPI_SAMPLECLOCK_SOURCE_AESEBU_INPUT)
has_aes_in = 1;
if (source == HPI_SAMPLECLOCK_SOURCE_LOCAL)
clkcache->has_local = 1;
}
if (has_aes_in)
/* already will have picked up index 0 above */
for (j = 1; j < 8; j++) {
if (hpi_sample_clock_query_source_index(ss, hSC,
j, HPI_SAMPLECLOCK_SOURCE_AESEBU_INPUT,
&source))
break;
clkcache->s[i].source =
HPI_SAMPLECLOCK_SOURCE_AESEBU_INPUT;
clkcache->s[i].index = j;
clkcache->s[i].name = sampleclock_sources[
j+HPI_SAMPLECLOCK_SOURCE_LAST];
i++;
}
clkcache->count = i;
asihpi_ctl_init(&snd_control, hpi_ctl, "source");
snd_control.access = SNDRV_CTL_ELEM_ACCESS_READWRITE ;
snd_control.info = snd_asihpi_clksrc_info;
snd_control.get = snd_asihpi_clksrc_get;
snd_control.put = snd_asihpi_clksrc_put;
if (ctl_add(card, &snd_control, asihpi) < 0)
return -EINVAL;
if (clkcache->has_local) {
asihpi_ctl_init(&snd_control, hpi_ctl, "local_rate");
snd_control.access = SNDRV_CTL_ELEM_ACCESS_READWRITE ;
snd_control.info = snd_asihpi_clklocal_info;
snd_control.get = snd_asihpi_clklocal_get;
snd_control.put = snd_asihpi_clklocal_put;
if (ctl_add(card, &snd_control, asihpi) < 0)
return -EINVAL;
}
asihpi_ctl_init(&snd_control, hpi_ctl, "rate");
snd_control.access =
SNDRV_CTL_ELEM_ACCESS_VOLATILE | SNDRV_CTL_ELEM_ACCESS_READ;
snd_control.info = snd_asihpi_clkrate_info;
snd_control.get = snd_asihpi_clkrate_get;
return ctl_add(card, &snd_control, asihpi);
}
/*------------------------------------------------------------
Mixer
------------------------------------------------------------*/
static int __devinit snd_card_asihpi_mixer_new(struct snd_card_asihpi *asihpi)
{
struct snd_card *card = asihpi->card;
unsigned int idx = 0;
unsigned int subindex = 0;
int err;
struct hpi_control hpi_ctl, prev_ctl;
if (snd_BUG_ON(!asihpi))
return -EINVAL;
strcpy(card->mixername, "asihpi mixer");
err =
hpi_mixer_open(ss, asihpi->adapter_index,
&asihpi->h_mixer);
hpi_handle_error(err);
if (err)
return -err;
for (idx = 0; idx < 2000; idx++) {
err = hpi_mixer_get_control_by_index(
ss, asihpi->h_mixer,
idx,
&hpi_ctl.src_node_type,
&hpi_ctl.src_node_index,
&hpi_ctl.dst_node_type,
&hpi_ctl.dst_node_index,
&hpi_ctl.control_type,
&hpi_ctl.h_control);
if (err) {
if (err == HPI_ERROR_CONTROL_DISABLED) {
if (mixer_dump)
snd_printk(KERN_INFO
"disabled HPI control(%d)\n",
idx);
continue;
} else
break;
}
hpi_ctl.src_node_type -= HPI_SOURCENODE_BASE;
hpi_ctl.dst_node_type -= HPI_DESTNODE_BASE;
/* ASI50xx in SSX mode has multiple meters on the same node.
Use subindex to create distinct ALSA controls
for any duplicated controls.
*/
if ((hpi_ctl.control_type == prev_ctl.control_type) &&
(hpi_ctl.src_node_type == prev_ctl.src_node_type) &&
(hpi_ctl.src_node_index == prev_ctl.src_node_index) &&
(hpi_ctl.dst_node_type == prev_ctl.dst_node_type) &&
(hpi_ctl.dst_node_index == prev_ctl.dst_node_index))
subindex++;
else
subindex = 0;
prev_ctl = hpi_ctl;
switch (hpi_ctl.control_type) {
case HPI_CONTROL_VOLUME:
err = snd_asihpi_volume_add(asihpi, &hpi_ctl);
break;
case HPI_CONTROL_LEVEL:
err = snd_asihpi_level_add(asihpi, &hpi_ctl);
break;
case HPI_CONTROL_MULTIPLEXER:
err = snd_asihpi_mux_add(asihpi, &hpi_ctl);
break;
case HPI_CONTROL_CHANNEL_MODE:
err = snd_asihpi_cmode_add(asihpi, &hpi_ctl);
break;
case HPI_CONTROL_METER:
err = snd_asihpi_meter_add(asihpi, &hpi_ctl, subindex);
break;
case HPI_CONTROL_SAMPLECLOCK:
err = snd_asihpi_sampleclock_add(
asihpi, &hpi_ctl);
break;
case HPI_CONTROL_CONNECTION: /* ignore these */
continue;
case HPI_CONTROL_TUNER:
err = snd_asihpi_tuner_add(asihpi, &hpi_ctl);
break;
case HPI_CONTROL_AESEBU_TRANSMITTER:
err = snd_asihpi_aesebu_tx_add(asihpi, &hpi_ctl);
break;
case HPI_CONTROL_AESEBU_RECEIVER:
err = snd_asihpi_aesebu_rx_add(asihpi, &hpi_ctl);
break;
case HPI_CONTROL_VOX:
case HPI_CONTROL_BITSTREAM:
case HPI_CONTROL_MICROPHONE:
case HPI_CONTROL_PARAMETRIC_EQ:
case HPI_CONTROL_COMPANDER:
default:
if (mixer_dump)
snd_printk(KERN_INFO
"untranslated HPI control"
"(%d) %d %d %d %d %d\n",
idx,
hpi_ctl.control_type,
hpi_ctl.src_node_type,
hpi_ctl.src_node_index,
hpi_ctl.dst_node_type,
hpi_ctl.dst_node_index);
continue;
};
if (err < 0)
return err;
}
if (HPI_ERROR_INVALID_OBJ_INDEX != err)
hpi_handle_error(err);
snd_printk(KERN_INFO "%d mixer controls found\n", idx);
return 0;
}
/*------------------------------------------------------------
/proc interface
------------------------------------------------------------*/
static void
snd_asihpi_proc_read(struct snd_info_entry *entry,
struct snd_info_buffer *buffer)
{
struct snd_card_asihpi *asihpi = entry->private_data;
u16 version;
u32 h_control;
u32 rate = 0;
u16 source = 0;
int err;
snd_iprintf(buffer, "ASIHPI driver proc file\n");
snd_iprintf(buffer,
"adapter ID=%4X\n_index=%d\n"
"num_outstreams=%d\n_num_instreams=%d\n",
asihpi->type, asihpi->adapter_index,
asihpi->num_outstreams, asihpi->num_instreams);
version = asihpi->version;
snd_iprintf(buffer,
"serial#=%d\n_hw version %c%d\nDSP code version %03d\n",
asihpi->serial_number, ((version >> 3) & 0xf) + 'A',
version & 0x7,
((version >> 13) * 100) + ((version >> 7) & 0x3f));
err = hpi_mixer_get_control(ss, asihpi->h_mixer,
HPI_SOURCENODE_CLOCK_SOURCE, 0, 0, 0,
HPI_CONTROL_SAMPLECLOCK, &h_control);
if (!err) {
err = hpi_sample_clock_get_sample_rate(ss,
h_control, &rate);
err += hpi_sample_clock_get_source(ss, h_control, &source);
if (!err)
snd_iprintf(buffer, "sample_clock=%d_hz, source %s\n",
rate, sampleclock_sources[source]);
}
}
static void __devinit snd_asihpi_proc_init(struct snd_card_asihpi *asihpi)
{
struct snd_info_entry *entry;
if (!snd_card_proc_new(asihpi->card, "info", &entry))
snd_info_set_text_ops(entry, asihpi, snd_asihpi_proc_read);
}
/*------------------------------------------------------------
HWDEP
------------------------------------------------------------*/
static int snd_asihpi_hpi_open(struct snd_hwdep *hw, struct file *file)
{
if (enable_hpi_hwdep)
return 0;
else
return -ENODEV;
}
static int snd_asihpi_hpi_release(struct snd_hwdep *hw, struct file *file)
{
if (enable_hpi_hwdep)
return asihpi_hpi_release(file);
else
return -ENODEV;
}
static int snd_asihpi_hpi_ioctl(struct snd_hwdep *hw, struct file *file,
unsigned int cmd, unsigned long arg)
{
if (enable_hpi_hwdep)
return asihpi_hpi_ioctl(file, cmd, arg);
else
return -ENODEV;
}
/* results in /dev/snd/hwC#D0 file for each card with index #
also /proc/asound/hwdep will contain '#-00: asihpi (HPI) for each card'
*/
static int __devinit snd_asihpi_hpi_new(struct snd_card_asihpi *asihpi,
int device, struct snd_hwdep **rhwdep)
{
struct snd_hwdep *hw;
int err;
if (rhwdep)
*rhwdep = NULL;
err = snd_hwdep_new(asihpi->card, "HPI", device, &hw);
if (err < 0)
return err;
strcpy(hw->name, "asihpi (HPI)");
hw->iface = SNDRV_HWDEP_IFACE_LAST;
hw->ops.open = snd_asihpi_hpi_open;
hw->ops.ioctl = snd_asihpi_hpi_ioctl;
hw->ops.release = snd_asihpi_hpi_release;
hw->private_data = asihpi;
if (rhwdep)
*rhwdep = hw;
return 0;
}
/*------------------------------------------------------------
CARD
------------------------------------------------------------*/
static int __devinit snd_asihpi_probe(struct pci_dev *pci_dev,
const struct pci_device_id *pci_id)
{
int err;
u16 version;
int pcm_substreams;
struct hpi_adapter *hpi_card;
struct snd_card *card;
struct snd_card_asihpi *asihpi;
u32 h_control;
u32 h_stream;
static int dev;
if (dev >= SNDRV_CARDS)
return -ENODEV;
/* Should this be enable[hpi_card->index] ? */
if (!enable[dev]) {
dev++;
return -ENOENT;
}
err = asihpi_adapter_probe(pci_dev, pci_id);
if (err < 0)
return err;
hpi_card = pci_get_drvdata(pci_dev);
/* first try to give the card the same index as its hardware index */
err = snd_card_create(hpi_card->index,
id[hpi_card->index], THIS_MODULE,
sizeof(struct snd_card_asihpi),
&card);
if (err < 0) {
/* if that fails, try the default index==next available */
err =
snd_card_create(index[dev], id[dev],
THIS_MODULE,
sizeof(struct snd_card_asihpi),
&card);
if (err < 0)
return err;
snd_printk(KERN_WARNING
"**** WARNING **** adapter index %d->ALSA index %d\n",
hpi_card->index, card->number);
}
asihpi = (struct snd_card_asihpi *) card->private_data;
asihpi->card = card;
asihpi->pci = hpi_card->pci;
asihpi->adapter_index = hpi_card->index;
hpi_handle_error(hpi_adapter_get_info(ss,
asihpi->adapter_index,
&asihpi->num_outstreams,
&asihpi->num_instreams,
&asihpi->version,
&asihpi->serial_number, &asihpi->type));
version = asihpi->version;
snd_printk(KERN_INFO "adapter ID=%4X index=%d num_outstreams=%d "
"num_instreams=%d S/N=%d\n"
"hw version %c%d DSP code version %03d\n",
asihpi->type, asihpi->adapter_index,
asihpi->num_outstreams,
asihpi->num_instreams, asihpi->serial_number,
((version >> 3) & 0xf) + 'A',
version & 0x7,
((version >> 13) * 100) + ((version >> 7) & 0x3f));
pcm_substreams = asihpi->num_outstreams;
if (pcm_substreams < asihpi->num_instreams)
pcm_substreams = asihpi->num_instreams;
err = hpi_adapter_get_property(ss, asihpi->adapter_index,
HPI_ADAPTER_PROPERTY_CAPS1,
NULL, &asihpi->support_grouping);
if (err)
asihpi->support_grouping = 0;
err = hpi_adapter_get_property(ss, asihpi->adapter_index,
HPI_ADAPTER_PROPERTY_CAPS2,
&asihpi->support_mrx, NULL);
if (err)
asihpi->support_mrx = 0;
err = hpi_adapter_get_property(ss, asihpi->adapter_index,
HPI_ADAPTER_PROPERTY_INTERVAL,
NULL, &asihpi->update_interval_frames);
if (err)
asihpi->update_interval_frames = 512;
hpi_handle_error(hpi_instream_open(ss, asihpi->adapter_index,
0, &h_stream));
err = hpi_instream_host_buffer_free(ss, h_stream);
asihpi->support_mmap = (!err);
hpi_handle_error(hpi_instream_close(ss, h_stream));
err = hpi_adapter_get_property(ss, asihpi->adapter_index,
HPI_ADAPTER_PROPERTY_CURCHANNELS,
&asihpi->in_max_chans, &asihpi->out_max_chans);
if (err) {
asihpi->in_max_chans = 2;
asihpi->out_max_chans = 2;
}
snd_printk(KERN_INFO "supports mmap:%d grouping:%d mrx:%d\n",
asihpi->support_mmap,
asihpi->support_grouping,
asihpi->support_mrx
);
err = snd_card_asihpi_pcm_new(asihpi, 0, pcm_substreams);
if (err < 0) {
snd_printk(KERN_ERR "pcm_new failed\n");
goto __nodev;
}
err = snd_card_asihpi_mixer_new(asihpi);
if (err < 0) {
snd_printk(KERN_ERR "mixer_new failed\n");
goto __nodev;
}
err = hpi_mixer_get_control(ss, asihpi->h_mixer,
HPI_SOURCENODE_CLOCK_SOURCE, 0, 0, 0,
HPI_CONTROL_SAMPLECLOCK, &h_control);
if (!err)
err = hpi_sample_clock_set_local_rate(
ss, h_control, adapter_fs);
snd_asihpi_proc_init(asihpi);
/* always create, can be enabled or disabled dynamically
by enable_hwdep module param*/
snd_asihpi_hpi_new(asihpi, 0, NULL);
if (asihpi->support_mmap)
strcpy(card->driver, "ASIHPI-MMAP");
else
strcpy(card->driver, "ASIHPI");
sprintf(card->shortname, "AudioScience ASI%4X", asihpi->type);
sprintf(card->longname, "%s %i",
card->shortname, asihpi->adapter_index);
err = snd_card_register(card);
if (!err) {
hpi_card->snd_card_asihpi = card;
dev++;
return 0;
}
__nodev:
snd_card_free(card);
snd_printk(KERN_ERR "snd_asihpi_probe error %d\n", err);
return err;
}
static void __devexit snd_asihpi_remove(struct pci_dev *pci_dev)
{
struct hpi_adapter *hpi_card = pci_get_drvdata(pci_dev);
snd_card_free(hpi_card->snd_card_asihpi);
hpi_card->snd_card_asihpi = NULL;
asihpi_adapter_remove(pci_dev);
}
static DEFINE_PCI_DEVICE_TABLE(asihpi_pci_tbl) = {
{HPI_PCI_VENDOR_ID_TI, HPI_PCI_DEV_ID_DSP6205,
HPI_PCI_VENDOR_ID_AUDIOSCIENCE, PCI_ANY_ID, 0, 0,
(kernel_ulong_t)HPI_6205},
{HPI_PCI_VENDOR_ID_TI, HPI_PCI_DEV_ID_PCI2040,
HPI_PCI_VENDOR_ID_AUDIOSCIENCE, PCI_ANY_ID, 0, 0,
(kernel_ulong_t)HPI_6000},
{0,}
};
MODULE_DEVICE_TABLE(pci, asihpi_pci_tbl);
static struct pci_driver driver = {
.name = "asihpi",
.id_table = asihpi_pci_tbl,
.probe = snd_asihpi_probe,
.remove = __devexit_p(snd_asihpi_remove),
#ifdef CONFIG_PM
/* .suspend = snd_asihpi_suspend,
.resume = snd_asihpi_resume, */
#endif
};
static int __init snd_asihpi_init(void)
{
asihpi_init();
return pci_register_driver(&driver);
}
static void __exit snd_asihpi_exit(void)
{
pci_unregister_driver(&driver);
asihpi_exit();
}
module_init(snd_asihpi_init)
module_exit(snd_asihpi_exit)
/******************************************************************************
AudioScience HPI driver
Copyright (C) 1997-2010 AudioScience Inc. <support@audioscience.com>
This program is free software; you can redistribute it and/or modify
it under the terms of version 2 of the GNU General Public License as
published by the Free Software Foundation;
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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
/** \file hpi.h
AudioScience Hardware Programming Interface (HPI)
public API definition.
The HPI is a low-level hardware abstraction layer to all
AudioScience digital audio adapters
*/
/*
You must define one operating system that the HPI is to be compiled under
HPI_OS_WIN32_USER 32bit Windows
HPI_OS_DSP_C6000 DSP TI C6000 (automatically set)
HPI_OS_WDM Windows WDM kernel driver
HPI_OS_LINUX Linux userspace
HPI_OS_LINUX_KERNEL Linux kernel (automatically set)
(C) Copyright AudioScience Inc. 1998-2010
******************************************************************************/
#ifndef _HPI_H_
#define _HPI_H_
/* HPI Version
If HPI_VER_MINOR is odd then its a development release not intended for the
public. If HPI_VER_MINOR is even then is a release version
i.e 3.05.02 is a development version
*/
#define HPI_VERSION_CONSTRUCTOR(maj, min, rel) \
((maj << 16) + (min << 8) + rel)
#define HPI_VER_MAJOR(v) ((int)(v >> 16))
#define HPI_VER_MINOR(v) ((int)((v >> 8) & 0xFF))
#define HPI_VER_RELEASE(v) ((int)(v & 0xFF))
/* Use single digits for versions less that 10 to avoid octal. */
#define HPI_VER HPI_VERSION_CONSTRUCTOR(4L, 3, 18)
/* Library version as documented in hpi-api-versions.txt */
#define HPI_LIB_VER HPI_VERSION_CONSTRUCTOR(9, 0, 0)
#include <linux/types.h>
#define HPI_EXCLUDE_DEPRECATED
/******************************************************************************/
/******************************************************************************/
/******** HPI API DEFINITIONS *****/
/******************************************************************************/
/******************************************************************************/
/*******************************************/
/** Audio format types
\ingroup stream
*/
enum HPI_FORMATS {
/** Used internally on adapter. */
HPI_FORMAT_MIXER_NATIVE = 0,
/** 8-bit unsigned PCM. Windows equivalent is WAVE_FORMAT_PCM. */
HPI_FORMAT_PCM8_UNSIGNED = 1,
/** 16-bit signed PCM. Windows equivalent is WAVE_FORMAT_PCM. */
HPI_FORMAT_PCM16_SIGNED = 2,
/** MPEG-1 Layer-1. */
HPI_FORMAT_MPEG_L1 = 3,
/** MPEG-1 Layer-2.
Windows equivalent is WAVE_FORMAT_MPEG.
The following table shows what combinations of mode and bitrate are possible:
<table border=1 cellspacing=0 cellpadding=5>
<tr>
<td><p><b>Bitrate (kbs)</b></p>
<td><p><b>Mono</b></p>
<td><p><b>Stereo,<br>Joint Stereo or<br>Dual Channel</b></p>
<tr><td>32<td>X<td>_
<tr><td>40<td>_<td>_
<tr><td>48<td>X<td>_
<tr><td>56<td>X<td>_
<tr><td>64<td>X<td>X
<tr><td>80<td>X<td>_
<tr><td>96<td>X<td>X
<tr><td>112<td>X<td>X
<tr><td>128<td>X<td>X
<tr><td>160<td>X<td>X
<tr><td>192<td>X<td>X
<tr><td>224<td>_<td>X
<tr><td>256<td>-<td>X
<tr><td>320<td>-<td>X
<tr><td>384<td>_<td>X
</table>
*/
HPI_FORMAT_MPEG_L2 = 4,
/** MPEG-1 Layer-3.
Windows equivalent is WAVE_FORMAT_MPEG.
The following table shows what combinations of mode and bitrate are possible:
<table border=1 cellspacing=0 cellpadding=5>
<tr>
<td><p><b>Bitrate (kbs)</b></p>
<td><p><b>Mono<br>Stereo @ 8,<br>11.025 and<br>12kHz*</b></p>
<td><p><b>Mono<br>Stereo @ 16,<br>22.050 and<br>24kHz*</b></p>
<td><p><b>Mono<br>Stereo @ 32,<br>44.1 and<br>48kHz</b></p>
<tr><td>16<td>X<td>X<td>_
<tr><td>24<td>X<td>X<td>_
<tr><td>32<td>X<td>X<td>X
<tr><td>40<td>X<td>X<td>X
<tr><td>48<td>X<td>X<td>X
<tr><td>56<td>X<td>X<td>X
<tr><td>64<td>X<td>X<td>X
<tr><td>80<td>_<td>X<td>X
<tr><td>96<td>_<td>X<td>X
<tr><td>112<td>_<td>X<td>X
<tr><td>128<td>_<td>X<td>X
<tr><td>144<td>_<td>X<td>_
<tr><td>160<td>_<td>X<td>X
<tr><td>192<td>_<td>_<td>X
<tr><td>224<td>_<td>_<td>X
<tr><td>256<td>-<td>_<td>X
<tr><td>320<td>-<td>_<td>X
</table>
\b * Available on the ASI6000 series only
*/
HPI_FORMAT_MPEG_L3 = 5,
/** Dolby AC-2. */
HPI_FORMAT_DOLBY_AC2 = 6,
/** Dolbt AC-3. */
HPI_FORMAT_DOLBY_AC3 = 7,
/** 16-bit PCM big-endian. */
HPI_FORMAT_PCM16_BIGENDIAN = 8,
/** TAGIT-1 algorithm - hits. */
HPI_FORMAT_AA_TAGIT1_HITS = 9,
/** TAGIT-1 algorithm - inserts. */
HPI_FORMAT_AA_TAGIT1_INSERTS = 10,
/** 32-bit signed PCM. Windows equivalent is WAVE_FORMAT_PCM.
Each sample is a 32bit word. The most significant 24 bits contain a 24-bit
sample and the least significant 8 bits are set to 0.
*/
HPI_FORMAT_PCM32_SIGNED = 11,
/** Raw bitstream - unknown format. */
HPI_FORMAT_RAW_BITSTREAM = 12,
/** TAGIT-1 algorithm hits - extended. */
HPI_FORMAT_AA_TAGIT1_HITS_EX1 = 13,
/** 32-bit PCM as an IEEE float. Windows equivalent is WAVE_FORMAT_IEEE_FLOAT.
Each sample is a 32bit word in IEEE754 floating point format.
The range is +1.0 to -1.0, which corresponds to digital fullscale.
*/
HPI_FORMAT_PCM32_FLOAT = 14,
/** 24-bit PCM signed. Windows equivalent is WAVE_FORMAT_PCM. */
HPI_FORMAT_PCM24_SIGNED = 15,
/** OEM format 1 - private. */
HPI_FORMAT_OEM1 = 16,
/** OEM format 2 - private. */
HPI_FORMAT_OEM2 = 17,
/** Undefined format. */
HPI_FORMAT_UNDEFINED = 0xffff
};
/******************************************* in/out Stream states */
/*******************************************/
/** Stream States
\ingroup stream
*/
enum HPI_STREAM_STATES {
/** State stopped - stream is stopped. */
HPI_STATE_STOPPED = 1,
/** State playing - stream is playing audio. */
HPI_STATE_PLAYING = 2,
/** State recording - stream is recording. */
HPI_STATE_RECORDING = 3,
/** State drained - playing stream ran out of data to play. */
HPI_STATE_DRAINED = 4,
/** State generate sine - to be implemented. */
HPI_STATE_SINEGEN = 5,
/** State wait - used for inter-card sync to mean waiting for all
cards to be ready. */
HPI_STATE_WAIT = 6
};
/******************************************* mixer source node types */
/** Source node types
\ingroup mixer
*/
enum HPI_SOURCENODES {
/** This define can be used instead of 0 to indicate
that there is no valid source node. A control that
exists on a destination node can be searched for using a source
node value of either 0, or HPI_SOURCENODE_NONE */
HPI_SOURCENODE_NONE = 100,
/** \deprecated Use HPI_SOURCENODE_NONE instead. */
HPI_SOURCENODE_BASE = 100,
/** Out Stream (Play) node. */
HPI_SOURCENODE_OSTREAM = 101,
/** Line in node - could be analog, AES/EBU or network. */
HPI_SOURCENODE_LINEIN = 102,
HPI_SOURCENODE_AESEBU_IN = 103, /**< AES/EBU input node. */
HPI_SOURCENODE_TUNER = 104, /**< tuner node. */
HPI_SOURCENODE_RF = 105, /**< RF input node. */
HPI_SOURCENODE_CLOCK_SOURCE = 106, /**< clock source node. */
HPI_SOURCENODE_RAW_BITSTREAM = 107, /**< raw bitstream node. */
HPI_SOURCENODE_MICROPHONE = 108, /**< microphone node. */
/** Cobranet input node -
Audio samples come from the Cobranet network and into the device. */
HPI_SOURCENODE_COBRANET = 109,
HPI_SOURCENODE_ANALOG = 110, /**< analog input node. */
HPI_SOURCENODE_ADAPTER = 111, /**< adapter node. */
/* !!!Update this AND hpidebug.h if you add a new sourcenode type!!! */
HPI_SOURCENODE_LAST_INDEX = 111 /**< largest ID */
/* AX6 max sourcenode types = 15 */
};
/******************************************* mixer dest node types */
/** Destination node types
\ingroup mixer
*/
enum HPI_DESTNODES {
/** This define can be used instead of 0 to indicate
that there is no valid destination node. A control that
exists on a source node can be searched for using a destination
node value of either 0, or HPI_DESTNODE_NONE */
HPI_DESTNODE_NONE = 200,
/** \deprecated Use HPI_DESTNODE_NONE instead. */
HPI_DESTNODE_BASE = 200,
/** In Stream (Record) node. */
HPI_DESTNODE_ISTREAM = 201,
HPI_DESTNODE_LINEOUT = 202, /**< line out node. */
HPI_DESTNODE_AESEBU_OUT = 203, /**< AES/EBU output node. */
HPI_DESTNODE_RF = 204, /**< RF output node. */
HPI_DESTNODE_SPEAKER = 205, /**< speaker output node. */
/** Cobranet output node -
Audio samples from the device are sent out on the Cobranet network.*/
HPI_DESTNODE_COBRANET = 206,
HPI_DESTNODE_ANALOG = 207, /**< analog output node. */
/* !!!Update this AND hpidebug.h if you add a new destnode type!!! */
HPI_DESTNODE_LAST_INDEX = 207 /**< largest ID */
/* AX6 max destnode types = 15 */
};
/*******************************************/
/** Mixer control types
\ingroup mixer
*/
enum HPI_CONTROLS {
HPI_CONTROL_GENERIC = 0, /**< generic control. */
HPI_CONTROL_CONNECTION = 1, /**< A connection between nodes. */
HPI_CONTROL_VOLUME = 2, /**< volume control - works in dB_fs. */
HPI_CONTROL_METER = 3, /**< peak meter control. */
HPI_CONTROL_MUTE = 4, /*mute control - not used at present. */
HPI_CONTROL_MULTIPLEXER = 5, /**< multiplexer control. */
HPI_CONTROL_AESEBU_TRANSMITTER = 6, /**< AES/EBU transmitter control. */
HPI_CONTROL_AESEBUTX = HPI_CONTROL_AESEBU_TRANSMITTER,
HPI_CONTROL_AESEBU_RECEIVER = 7, /**< AES/EBU receiver control. */
HPI_CONTROL_AESEBURX = HPI_CONTROL_AESEBU_RECEIVER,
HPI_CONTROL_LEVEL = 8, /**< level/trim control - works in d_bu. */
HPI_CONTROL_TUNER = 9, /**< tuner control. */
/* HPI_CONTROL_ONOFFSWITCH = 10 */
HPI_CONTROL_VOX = 11, /**< vox control. */
/* HPI_CONTROL_AES18_TRANSMITTER = 12 */
/* HPI_CONTROL_AES18_RECEIVER = 13 */
/* HPI_CONTROL_AES18_BLOCKGENERATOR = 14 */
HPI_CONTROL_CHANNEL_MODE = 15, /**< channel mode control. */
HPI_CONTROL_BITSTREAM = 16, /**< bitstream control. */
HPI_CONTROL_SAMPLECLOCK = 17, /**< sample clock control. */
HPI_CONTROL_MICROPHONE = 18, /**< microphone control. */
HPI_CONTROL_PARAMETRIC_EQ = 19, /**< parametric EQ control. */
HPI_CONTROL_EQUALIZER = HPI_CONTROL_PARAMETRIC_EQ,
HPI_CONTROL_COMPANDER = 20, /**< compander control. */
HPI_CONTROL_COBRANET = 21, /**< cobranet control. */
HPI_CONTROL_TONEDETECTOR = 22, /**< tone detector control. */
HPI_CONTROL_SILENCEDETECTOR = 23, /**< silence detector control. */
HPI_CONTROL_PAD = 24, /**< tuner PAD control. */
HPI_CONTROL_SRC = 25, /**< samplerate converter control. */
HPI_CONTROL_UNIVERSAL = 26, /**< universal control. */
/* !!! Update this AND hpidebug.h if you add a new control type!!!*/
HPI_CONTROL_LAST_INDEX = 26 /**<highest control type ID */
/* WARNING types 256 or greater impact bit packing in all AX6 DSP code */
};
/* Shorthand names that match attribute names */
/******************************************* ADAPTER ATTRIBUTES ****/
/** Adapter properties
These are used in HPI_AdapterSetProperty() and HPI_AdapterGetProperty()
\ingroup adapter
*/
enum HPI_ADAPTER_PROPERTIES {
/** \internal Used in dwProperty field of HPI_AdapterSetProperty() and
HPI_AdapterGetProperty(). This errata applies to all ASI6000 cards with both
analog and digital outputs. The CS4224 A/D+D/A has a one sample delay between
left and right channels on both its input (ADC) and output (DAC).
More details are available in Cirrus Logic errata ER284B2.
PDF available from www.cirrus.com, released by Cirrus in 2001.
*/
HPI_ADAPTER_PROPERTY_ERRATA_1 = 1,
/** Adapter grouping property
Indicates whether the adapter supports the grouping API (for ASIO and SSX2)
*/
HPI_ADAPTER_PROPERTY_GROUPING = 2,
/** Driver SSX2 property
Tells the kernel driver to turn on SSX2 stream mapping.
This feature is not used by the DSP. In fact the call is completely processed
by the driver and is not passed on to the DSP at all.
*/
HPI_ADAPTER_PROPERTY_ENABLE_SSX2 = 3,
/** Adapter SSX2 property
Indicates the state of the adapter's SSX2 setting. This setting is stored in
non-volatile memory on the adapter. A typical call sequence would be to use
HPI_ADAPTER_PROPERTY_SSX2_SETTING to set SSX2 on the adapter and then to reload
the driver. The driver would query HPI_ADAPTER_PROPERTY_SSX2_SETTING during startup
and if SSX2 is set, it would then call HPI_ADAPTER_PROPERTY_ENABLE_SSX2 to enable
SSX2 stream mapping within the kernel level of the driver.
*/
HPI_ADAPTER_PROPERTY_SSX2_SETTING = 4,
/** Base number for readonly properties */
HPI_ADAPTER_PROPERTY_READONLYBASE = 256,
/** Readonly adapter latency property.
This property returns in the input and output latency in samples.
Property 1 is the estimated input latency
in samples, while Property 2 is that output latency in samples.
*/
HPI_ADAPTER_PROPERTY_LATENCY = 256,
/** Readonly adapter granularity property.
The granulariy is the smallest size chunk of stereo samples that is processed by
the adapter.
This property returns the record granularity in samples in Property 1.
Property 2 returns the play granularity.
*/
HPI_ADAPTER_PROPERTY_GRANULARITY = 257,
/** Readonly adapter number of current channels property.
Property 1 is the number of record channels per record device.
Property 2 is the number of play channels per playback device.*/
HPI_ADAPTER_PROPERTY_CURCHANNELS = 258,
/** Readonly adapter software version.
The SOFTWARE_VERSION property returns the version of the software running
on the adapter as Major.Minor.Release.
Property 1 contains Major in bits 15..8 and Minor in bits 7..0.
Property 2 contains Release in bits 7..0. */
HPI_ADAPTER_PROPERTY_SOFTWARE_VERSION = 259,
/** Readonly adapter MAC address MSBs.
The MAC_ADDRESS_MSB property returns
the most significant 32 bits of the MAC address.
Property 1 contains bits 47..32 of the MAC address.
Property 2 contains bits 31..16 of the MAC address. */
HPI_ADAPTER_PROPERTY_MAC_ADDRESS_MSB = 260,
/** Readonly adapter MAC address LSBs
The MAC_ADDRESS_LSB property returns
the least significant 16 bits of the MAC address.
Property 1 contains bits 15..0 of the MAC address. */
HPI_ADAPTER_PROPERTY_MAC_ADDRESS_LSB = 261,
/** Readonly extended adapter type number
The EXTENDED_ADAPTER_TYPE property returns the 4 digits of an extended
adapter type, i.e ASI8920-0022, 0022 is the extended type.
The digits are returned as ASCII characters rather than the hex digits that
are returned for the main type
Property 1 returns the 1st two (left most) digits, i.e "00"
in the example above, the upper byte being the left most digit.
Property 2 returns the 2nd two digits, i.e "22" in the example above*/
HPI_ADAPTER_PROPERTY_EXTENDED_ADAPTER_TYPE = 262,
/** Readonly debug log buffer information */
HPI_ADAPTER_PROPERTY_LOGTABLEN = 263,
HPI_ADAPTER_PROPERTY_LOGTABBEG = 264,
/** Readonly adapter IP address
For 192.168.1.101
Property 1 returns the 1st two (left most) digits, i.e 192*256 + 168
in the example above, the upper byte being the left most digit.
Property 2 returns the 2nd two digits, i.e 1*256 + 101 in the example above, */
HPI_ADAPTER_PROPERTY_IP_ADDRESS = 265,
/** Readonly adapter buffer processed count. Returns a buffer processed count
that is incremented every time all buffers for all streams are updated. This
is useful for checking completion of all stream operations across the adapter
when using grouped streams.
*/
HPI_ADAPTER_PROPERTY_BUFFER_UPDATE_COUNT = 266,
/** Readonly mixer and stream intervals
These intervals are measured in mixer frames.
To convert to time, divide by the adapter samplerate.
The mixer interval is the number of frames processed in one mixer iteration.
The stream update interval is the interval at which streams check for and
process data, and BBM host buffer counters are updated.
Property 1 is the mixer interval in mixer frames.
Property 2 is the stream update interval in mixer frames.
*/
HPI_ADAPTER_PROPERTY_INTERVAL = 267,
/** Adapter capabilities 1
Property 1 - adapter can do multichannel (SSX1)
Property 2 - adapter can do stream grouping (supports SSX2)
*/
HPI_ADAPTER_PROPERTY_CAPS1 = 268,
/** Adapter capabilities 2
Property 1 - adapter can do samplerate conversion (MRX)
Property 2 - adapter can do timestretch (TSX)
*/
HPI_ADAPTER_PROPERTY_CAPS2 = 269
};
/** Adapter mode commands
Used in wQueryOrSet field of HPI_AdapterSetModeEx().
\ingroup adapter
*/
enum HPI_ADAPTER_MODE_CMDS {
HPI_ADAPTER_MODE_SET = 0,
HPI_ADAPTER_MODE_QUERY = 1
};
/** Adapter Modes
These are used by HPI_AdapterSetModeEx()
\warning - more than 16 possible modes breaks
a bitmask in the Windows WAVE DLL
\ingroup adapter
*/
enum HPI_ADAPTER_MODES {
/** 4 outstream mode.
- ASI6114: 1 instream
- ASI6044: 4 instreams
- ASI6012: 1 instream
- ASI6102: no instreams
- ASI6022, ASI6122: 2 instreams
- ASI5111, ASI5101: 2 instreams
- ASI652x, ASI662x: 2 instreams
- ASI654x, ASI664x: 4 instreams
*/
HPI_ADAPTER_MODE_4OSTREAM = 1,
/** 6 outstream mode.
- ASI6012: 1 instream,
- ASI6022, ASI6122: 2 instreams
- ASI652x, ASI662x: 4 instreams
*/
HPI_ADAPTER_MODE_6OSTREAM = 2,
/** 8 outstream mode.
- ASI6114: 8 instreams
- ASI6118: 8 instreams
- ASI6585: 8 instreams
*/
HPI_ADAPTER_MODE_8OSTREAM = 3,
/** 16 outstream mode.
- ASI6416 16 instreams
- ASI6518, ASI6618 16 instreams
- ASI6118 16 mono out and in streams
*/
HPI_ADAPTER_MODE_16OSTREAM = 4,
/** one outstream mode.
- ASI5111 1 outstream, 1 instream
*/
HPI_ADAPTER_MODE_1OSTREAM = 5,
/** ASI504X mode 1. 12 outstream, 4 instream 0 to 48kHz sample rates
(see ASI504X datasheet for more info).
*/
HPI_ADAPTER_MODE_1 = 6,
/** ASI504X mode 2. 4 outstreams, 4 instreams at 0 to 192kHz sample rates
(see ASI504X datasheet for more info).
*/
HPI_ADAPTER_MODE_2 = 7,
/** ASI504X mode 3. 4 outstreams, 4 instreams at 0 to 192kHz sample rates
(see ASI504X datasheet for more info).
*/
HPI_ADAPTER_MODE_3 = 8,
/** ASI504X multichannel mode.
2 outstreams -> 4 line outs = 1 to 8 channel streams),
4 lineins -> 1 instream (1 to 8 channel streams) at 0-48kHz.
For more info see the SSX Specification.
*/
HPI_ADAPTER_MODE_MULTICHANNEL = 9,
/** 12 outstream mode.
- ASI6514, ASI6614: 2 instreams
- ASI6540,ASI6544: 8 instreams
- ASI6640,ASI6644: 8 instreams
*/
HPI_ADAPTER_MODE_12OSTREAM = 10,
/** 9 outstream mode.
- ASI6044: 8 instreams
*/
HPI_ADAPTER_MODE_9OSTREAM = 11,
/** mono mode.
- ASI6416: 16 outstreams/instreams
- ASI5402: 2 outstreams/instreams
*/
HPI_ADAPTER_MODE_MONO = 12,
/** Low latency mode.
- ASI6416/ASI6316: 1 16 channel outstream and instream
*/
HPI_ADAPTER_MODE_LOW_LATENCY = 13
};
/* Note, adapters can have more than one capability -
encoding as bitfield is recommended. */
#define HPI_CAPABILITY_NONE (0)
#define HPI_CAPABILITY_MPEG_LAYER3 (1)
/* Set this equal to maximum capability index,
Must not be greater than 32 - see axnvdef.h */
#define HPI_CAPABILITY_MAX 1
/* #define HPI_CAPABILITY_AAC 2 */
/******************************************* STREAM ATTRIBUTES ****/
/** MPEG Ancillary Data modes
The mode for the ancillary data insertion or extraction to operate in.
\ingroup stream
*/
enum HPI_MPEG_ANC_MODES {
/** the MPEG frames have energy information stored in them (5 bytes per stereo frame, 3 per mono) */
HPI_MPEG_ANC_HASENERGY = 0,
/** the entire ancillary data field is taken up by data from the Anc data buffer
On encode, the encoder will insert the energy bytes before filling the remainder
of the ancillary data space with data from the ancillary data buffer.
*/
HPI_MPEG_ANC_RAW = 1
};
/** Ancillary Data Alignment
\ingroup instream
*/
enum HPI_ISTREAM_MPEG_ANC_ALIGNS {
/** data is packed against the end of data, then padded to the end of frame */
HPI_MPEG_ANC_ALIGN_LEFT = 0,
/** data is packed against the end of the frame */
HPI_MPEG_ANC_ALIGN_RIGHT = 1
};
/** MPEG modes
MPEG modes - can be used optionally for HPI_FormatCreate()
parameter dwAttributes.
Using any mode setting other than HPI_MPEG_MODE_DEFAULT
with single channel format will return an error.
\ingroup stream
*/
enum HPI_MPEG_MODES {
/** Causes the MPEG-1 Layer II bitstream to be recorded
in single_channel mode when the number of channels is 1 and in stereo when the
number of channels is 2. */
HPI_MPEG_MODE_DEFAULT = 0,
/** Standard stereo without joint-stereo compression */
HPI_MPEG_MODE_STEREO = 1,
/** Joint stereo */
HPI_MPEG_MODE_JOINTSTEREO = 2,
/** Left and Right channels are completely independent */
HPI_MPEG_MODE_DUALCHANNEL = 3
};
/******************************************* MIXER ATTRIBUTES ****/
/* \defgroup mixer_flags Mixer flags for HPI_MIXER_GET_CONTROL_MULTIPLE_VALUES
{
*/
#define HPI_MIXER_GET_CONTROL_MULTIPLE_CHANGED (0)
#define HPI_MIXER_GET_CONTROL_MULTIPLE_RESET (1)
/*}*/
/** Commands used by HPI_MixerStore()
\ingroup mixer
*/
enum HPI_MIXER_STORE_COMMAND {
/** Save all mixer control settings. */
HPI_MIXER_STORE_SAVE = 1,
/** Restore all controls from saved. */
HPI_MIXER_STORE_RESTORE = 2,
/** Delete saved control settings. */
HPI_MIXER_STORE_DELETE = 3,
/** Enable auto storage of some control settings. */
HPI_MIXER_STORE_ENABLE = 4,
/** Disable auto storage of some control settings. */
HPI_MIXER_STORE_DISABLE = 5,
/** Save the attributes of a single control. */
HPI_MIXER_STORE_SAVE_SINGLE = 6
};
/************************************* CONTROL ATTRIBUTE VALUES ****/
/** Used by mixer plugin enable functions
E.g. HPI_ParametricEQ_SetState()
\ingroup mixer
*/
enum HPI_SWITCH_STATES {
HPI_SWITCH_OFF = 0, /**< turn the mixer plugin on. */
HPI_SWITCH_ON = 1 /**< turn the mixer plugin off. */
};
/* Volume control special gain values */
/** volumes units are 100ths of a dB
\ingroup volume
*/
#define HPI_UNITS_PER_dB 100
/** turns volume control OFF or MUTE
\ingroup volume
*/
#define HPI_GAIN_OFF (-100 * HPI_UNITS_PER_dB)
/** value returned for no signal
\ingroup meter
*/
#define HPI_METER_MINIMUM (-150 * HPI_UNITS_PER_dB)
/** autofade profiles
\ingroup volume
*/
enum HPI_VOLUME_AUTOFADES {
/** log fade - dB attenuation changes linearly over time */
HPI_VOLUME_AUTOFADE_LOG = 2,
/** linear fade - amplitude changes linearly */
HPI_VOLUME_AUTOFADE_LINEAR = 3
};
/** The physical encoding format of the AESEBU I/O.
Used in HPI_AESEBU_Transmitter_SetFormat(), HPI_AESEBU_Receiver_SetFormat()
along with related Get and Query functions
\ingroup aestx
*/
enum HPI_AESEBU_FORMATS {
/** AES/EBU physical format - AES/EBU balanced "professional" */
HPI_AESEBU_FORMAT_AESEBU = 1,
/** AES/EBU physical format - S/PDIF unbalanced "consumer" */
HPI_AESEBU_FORMAT_SPDIF = 2
};
/** AES/EBU error status bits
Returned by HPI_AESEBU_Receiver_GetErrorStatus()
\ingroup aesrx
*/
enum HPI_AESEBU_ERRORS {
/** bit0: 1 when PLL is not locked */
HPI_AESEBU_ERROR_NOT_LOCKED = 0x01,
/** bit1: 1 when signal quality is poor */
HPI_AESEBU_ERROR_POOR_QUALITY = 0x02,
/** bit2: 1 when there is a parity error */
HPI_AESEBU_ERROR_PARITY_ERROR = 0x04,
/** bit3: 1 when there is a bi-phase coding violation */
HPI_AESEBU_ERROR_BIPHASE_VIOLATION = 0x08,
/** bit4: 1 when the validity bit is high */
HPI_AESEBU_ERROR_VALIDITY = 0x10,
/** bit5: 1 when the CRC error bit is high */
HPI_AESEBU_ERROR_CRC = 0x20
};
/** \addtogroup pad
\{
*/
/** The text string containing the station/channel combination. */
#define HPI_PAD_CHANNEL_NAME_LEN 16
/** The text string containing the artist. */
#define HPI_PAD_ARTIST_LEN 64
/** The text string containing the title. */
#define HPI_PAD_TITLE_LEN 64
/** The text string containing the comment. */
#define HPI_PAD_COMMENT_LEN 256
/** The PTY when the tuner has not recieved any PTY. */
#define HPI_PAD_PROGRAM_TYPE_INVALID 0xffff
/** \} */
/** Data types for PTY string translation.
\ingroup rds
*/
enum eHPI_RDS_type {
HPI_RDS_DATATYPE_RDS = 0, /**< RDS bitstream.*/
HPI_RDS_DATATYPE_RBDS = 1 /**< RBDS bitstream.*/
};
/** Tuner bands
Used for HPI_Tuner_SetBand(),HPI_Tuner_GetBand()
\ingroup tuner
*/
enum HPI_TUNER_BAND {
HPI_TUNER_BAND_AM = 1, /**< AM band */
HPI_TUNER_BAND_FM = 2, /**< FM band (mono) */
HPI_TUNER_BAND_TV_NTSC_M = 3, /**< NTSC-M TV band*/
HPI_TUNER_BAND_TV = 3, /* use TV_NTSC_M */
HPI_TUNER_BAND_FM_STEREO = 4, /**< FM band (stereo) */
HPI_TUNER_BAND_AUX = 5, /**< auxiliary input */
HPI_TUNER_BAND_TV_PAL_BG = 6, /**< PAL-B/G TV band*/
HPI_TUNER_BAND_TV_PAL_I = 7, /**< PAL-I TV band*/
HPI_TUNER_BAND_TV_PAL_DK = 8, /**< PAL-D/K TV band*/
HPI_TUNER_BAND_TV_SECAM_L = 9, /**< SECAM-L TV band*/
HPI_TUNER_BAND_LAST = 9 /**< the index of the last tuner band. */
};
/** Tuner mode attributes
Used by HPI_Tuner_SetMode(), HPI_Tuner_GetMode()
\ingroup tuner
*/
enum HPI_TUNER_MODES {
HPI_TUNER_MODE_RSS = 1, /**< control RSS */
HPI_TUNER_MODE_RDS = 2 /**< control RBDS/RDS */
};
/** Tuner mode attribute values
Used by HPI_Tuner_SetMode(), HPI_Tuner_GetMode()
\ingroup tuner
*/
enum HPI_TUNER_MODE_VALUES {
/* RSS attribute values */
HPI_TUNER_MODE_RSS_DISABLE = 0, /**< RSS disable */
HPI_TUNER_MODE_RSS_ENABLE = 1, /**< RSS enable */
/* RDS mode attributes */
HPI_TUNER_MODE_RDS_DISABLE = 0, /**< RDS - disabled */
HPI_TUNER_MODE_RDS_RDS = 1, /**< RDS - RDS mode */
HPI_TUNER_MODE_RDS_RBDS = 2 /**< RDS - RBDS mode */
};
/** Tuner Level settings
\ingroup tuner
*/
enum HPI_TUNER_LEVEL {
HPI_TUNER_LEVEL_AVERAGE = 0,
HPI_TUNER_LEVEL_RAW = 1
};
/** Tuner Status Bits
These bitfield values are returned by a call to HPI_Tuner_GetStatus().
Multiple fields are returned from a single call.
\ingroup tuner
*/
enum HPI_TUNER_STATUS_BITS {
HPI_TUNER_VIDEO_COLOR_PRESENT = 0x0001, /**< video color is present. */
HPI_TUNER_VIDEO_IS_60HZ = 0x0020, /**< 60 hz video detected. */
HPI_TUNER_VIDEO_HORZ_SYNC_MISSING = 0x0040, /**< video HSYNC is missing. */
HPI_TUNER_VIDEO_STATUS_VALID = 0x0100, /**< video status is valid. */
HPI_TUNER_PLL_LOCKED = 0x1000, /**< the tuner's PLL is locked. */
HPI_TUNER_FM_STEREO = 0x2000, /**< tuner reports back FM stereo. */
HPI_TUNER_DIGITAL = 0x0200, /**< tuner reports digital programming. */
HPI_TUNER_MULTIPROGRAM = 0x0400 /**< tuner reports multiple programs. */
};
/** Channel Modes
Used for HPI_ChannelModeSet/Get()
\ingroup channelmode
*/
enum HPI_CHANNEL_MODES {
/** Left channel out = left channel in, Right channel out = right channel in. */
HPI_CHANNEL_MODE_NORMAL = 1,
/** Left channel out = right channel in, Right channel out = left channel in. */
HPI_CHANNEL_MODE_SWAP = 2,
/** Left channel out = left channel in, Right channel out = left channel in. */
HPI_CHANNEL_MODE_LEFT_TO_STEREO = 3,
/** Left channel out = right channel in, Right channel out = right channel in.*/
HPI_CHANNEL_MODE_RIGHT_TO_STEREO = 4,
/** Left channel out = (left channel in + right channel in)/2,
Right channel out = mute. */
HPI_CHANNEL_MODE_STEREO_TO_LEFT = 5,
/** Left channel out = mute,
Right channel out = (right channel in + left channel in)/2. */
HPI_CHANNEL_MODE_STEREO_TO_RIGHT = 6,
HPI_CHANNEL_MODE_LAST = 6
};
/** SampleClock source values
\ingroup sampleclock
*/
enum HPI_SAMPLECLOCK_SOURCES {
/** The sampleclock output is derived from its local samplerate generator.
The local samplerate may be set using HPI_SampleClock_SetLocalRate(). */
HPI_SAMPLECLOCK_SOURCE_LOCAL = 1,
/** \deprecated Use HPI_SAMPLECLOCK_SOURCE_LOCAL instead */
HPI_SAMPLECLOCK_SOURCE_ADAPTER = 1,
/** The adapter is clocked from a dedicated AES/EBU SampleClock input.*/
HPI_SAMPLECLOCK_SOURCE_AESEBU_SYNC = 2,
/** From external wordclock connector */
HPI_SAMPLECLOCK_SOURCE_WORD = 3,
/** Board-to-board header */
HPI_SAMPLECLOCK_SOURCE_WORD_HEADER = 4,
/** FUTURE - SMPTE clock. */
HPI_SAMPLECLOCK_SOURCE_SMPTE = 5,
/** One of the aesebu inputs */
HPI_SAMPLECLOCK_SOURCE_AESEBU_INPUT = 6,
/** \deprecated The first aesebu input with a valid signal
Superseded by separate Auto enable flag
*/
HPI_SAMPLECLOCK_SOURCE_AESEBU_AUTO = 7,
/** From a network interface e.g. Cobranet or Livewire at either 48 or 96kHz */
HPI_SAMPLECLOCK_SOURCE_NETWORK = 8,
/** From previous adjacent module (ASI2416 only)*/
HPI_SAMPLECLOCK_SOURCE_PREV_MODULE = 10,
/*! Update this if you add a new clock source.*/
HPI_SAMPLECLOCK_SOURCE_LAST = 10
};
/** Equalizer filter types. Used by HPI_ParametricEQ_SetBand()
\ingroup parmeq
*/
enum HPI_FILTER_TYPE {
HPI_FILTER_TYPE_BYPASS = 0, /**< filter is turned off */
HPI_FILTER_TYPE_LOWSHELF = 1, /**< EQ low shelf */
HPI_FILTER_TYPE_HIGHSHELF = 2, /**< EQ high shelf */
HPI_FILTER_TYPE_EQ_BAND = 3, /**< EQ gain */
HPI_FILTER_TYPE_LOWPASS = 4, /**< standard low pass */
HPI_FILTER_TYPE_HIGHPASS = 5, /**< standard high pass */
HPI_FILTER_TYPE_BANDPASS = 6, /**< standard band pass */
HPI_FILTER_TYPE_BANDSTOP = 7 /**< standard band stop/notch */
};
/** Async Event sources
\ingroup async
*/
enum ASYNC_EVENT_SOURCES {
HPI_ASYNC_EVENT_GPIO = 1, /**< GPIO event. */
HPI_ASYNC_EVENT_SILENCE = 2, /**< silence event detected. */
HPI_ASYNC_EVENT_TONE = 3 /**< tone event detected. */
};
/*******************************************/
/** HPI Error codes
Almost all HPI functions return an error code
A return value of zero means there was no error.
Otherwise one of these error codes is returned.
Error codes can be converted to a descriptive string using HPI_GetErrorText()
\note When a new error code is added HPI_GetErrorText() MUST be updated.
\note Codes 1-100 are reserved for driver use
\ingroup utility
*/
enum HPI_ERROR_CODES {
/** Message type does not exist. */
HPI_ERROR_INVALID_TYPE = 100,
/** Object type does not exist. */
HPI_ERROR_INVALID_OBJ = 101,
/** Function does not exist. */
HPI_ERROR_INVALID_FUNC = 102,
/** The specified object (adapter/Stream) does not exist. */
HPI_ERROR_INVALID_OBJ_INDEX = 103,
/** Trying to access an object that has not been opened yet. */
HPI_ERROR_OBJ_NOT_OPEN = 104,
/** Trying to open an already open object. */
HPI_ERROR_OBJ_ALREADY_OPEN = 105,
/** PCI, ISA resource not valid. */
HPI_ERROR_INVALID_RESOURCE = 106,
/** GetInfo call from SubSysFindAdapters failed. */
HPI_ERROR_SUBSYSFINDADAPTERS_GETINFO = 107,
/** Default response was never updated with actual error code. */
HPI_ERROR_INVALID_RESPONSE = 108,
/** wSize field of response was not updated,
indicating that the message was not processed. */
HPI_ERROR_PROCESSING_MESSAGE = 109,
/** The network did not respond in a timely manner. */
HPI_ERROR_NETWORK_TIMEOUT = 110,
/** An HPI handle is invalid (uninitialised?). */
HPI_ERROR_INVALID_HANDLE = 111,
/** A function or attribute has not been implemented yet. */
HPI_ERROR_UNIMPLEMENTED = 112,
/** There are too many clients attempting to access a network resource. */
HPI_ERROR_NETWORK_TOO_MANY_CLIENTS = 113,
/** Response buffer passed to HPI_Message was smaller than returned response */
HPI_ERROR_RESPONSE_BUFFER_TOO_SMALL = 114,
/** The returned response did not match the sent message */
HPI_ERROR_RESPONSE_MISMATCH = 115,
/** Too many adapters.*/
HPI_ERROR_TOO_MANY_ADAPTERS = 200,
/** Bad adpater. */
HPI_ERROR_BAD_ADAPTER = 201,
/** Adapter number out of range or not set properly. */
HPI_ERROR_BAD_ADAPTER_NUMBER = 202,
/** 2 adapters with the same adapter number. */
HPI_DUPLICATE_ADAPTER_NUMBER = 203,
/** DSP code failed to bootload. */
HPI_ERROR_DSP_BOOTLOAD = 204,
/** Adapter failed DSP code self test. */
HPI_ERROR_DSP_SELFTEST = 205,
/** Couldn't find or open the DSP code file. */
HPI_ERROR_DSP_FILE_NOT_FOUND = 206,
/** Internal DSP hardware error. */
HPI_ERROR_DSP_HARDWARE = 207,
/** Could not allocate memory in DOS. */
HPI_ERROR_DOS_MEMORY_ALLOC = 208,
/** Could not allocate memory */
HPI_ERROR_MEMORY_ALLOC = 208,
/** Failed to correctly load/config PLD .*/
HPI_ERROR_PLD_LOAD = 209,
/** Unexpected end of file, block length too big etc. */
HPI_ERROR_DSP_FILE_FORMAT = 210,
/** Found but could not open DSP code file. */
HPI_ERROR_DSP_FILE_ACCESS_DENIED = 211,
/** First DSP code section header not found in DSP file. */
HPI_ERROR_DSP_FILE_NO_HEADER = 212,
/** File read operation on DSP code file failed. */
HPI_ERROR_DSP_FILE_READ_ERROR = 213,
/** DSP code for adapter family not found. */
HPI_ERROR_DSP_SECTION_NOT_FOUND = 214,
/** Other OS specific error opening DSP file. */
HPI_ERROR_DSP_FILE_OTHER_ERROR = 215,
/** Sharing violation opening DSP code file. */
HPI_ERROR_DSP_FILE_SHARING_VIOLATION = 216,
/** DSP code section header had size == 0. */
HPI_ERROR_DSP_FILE_NULL_HEADER = 217,
/** Base number for flash errors. */
HPI_ERROR_FLASH = 220,
/** Flash has bad checksum */
HPI_ERROR_BAD_CHECKSUM = (HPI_ERROR_FLASH + 1),
HPI_ERROR_BAD_SEQUENCE = (HPI_ERROR_FLASH + 2),
HPI_ERROR_FLASH_ERASE = (HPI_ERROR_FLASH + 3),
HPI_ERROR_FLASH_PROGRAM = (HPI_ERROR_FLASH + 4),
HPI_ERROR_FLASH_VERIFY = (HPI_ERROR_FLASH + 5),
HPI_ERROR_FLASH_TYPE = (HPI_ERROR_FLASH + 6),
HPI_ERROR_FLASH_START = (HPI_ERROR_FLASH + 7),
/** Reserved for OEMs. */
HPI_ERROR_RESERVED_1 = 290,
/** Stream does not exist. */
HPI_ERROR_INVALID_STREAM = 300,
/** Invalid compression format. */
HPI_ERROR_INVALID_FORMAT = 301,
/** Invalid format samplerate */
HPI_ERROR_INVALID_SAMPLERATE = 302,
/** Invalid format number of channels. */
HPI_ERROR_INVALID_CHANNELS = 303,
/** Invalid format bitrate. */
HPI_ERROR_INVALID_BITRATE = 304,
/** Invalid datasize used for stream read/write. */
HPI_ERROR_INVALID_DATASIZE = 305,
/** Stream buffer is full during stream write. */
HPI_ERROR_BUFFER_FULL = 306,
/** Stream buffer is empty during stream read. */
HPI_ERROR_BUFFER_EMPTY = 307,
/** Invalid datasize used for stream read/write. */
HPI_ERROR_INVALID_DATA_TRANSFER = 308,
/** Packet ordering error for stream read/write. */
HPI_ERROR_INVALID_PACKET_ORDER = 309,
/** Object can't do requested operation in its current
state, eg set format, change rec mux state while recording.*/
HPI_ERROR_INVALID_OPERATION = 310,
/** Where an SRG is shared amongst streams, an incompatible samplerate is one
that is different to any currently playing or recording stream. */
HPI_ERROR_INCOMPATIBLE_SAMPLERATE = 311,
/** Adapter mode is illegal.*/
HPI_ERROR_BAD_ADAPTER_MODE = 312,
/** There have been too many attempts to set the adapter's
capabilities (using bad keys), the card should be returned
to ASI if further capabilities updates are required */
HPI_ERROR_TOO_MANY_CAPABILITY_CHANGE_ATTEMPTS = 313,
/** Streams on different adapters cannot be grouped. */
HPI_ERROR_NO_INTERADAPTER_GROUPS = 314,
/** Streams on different DSPs cannot be grouped. */
HPI_ERROR_NO_INTERDSP_GROUPS = 315,
/** Invalid mixer node for this adapter. */
HPI_ERROR_INVALID_NODE = 400,
/** Invalid control. */
HPI_ERROR_INVALID_CONTROL = 401,
/** Invalid control value was passed. */
HPI_ERROR_INVALID_CONTROL_VALUE = 402,
/** Control attribute not supported by this control. */
HPI_ERROR_INVALID_CONTROL_ATTRIBUTE = 403,
/** Control is disabled. */
HPI_ERROR_CONTROL_DISABLED = 404,
/** I2C transaction failed due to a missing ACK. */
HPI_ERROR_CONTROL_I2C_MISSING_ACK = 405,
/** Control attribute is valid, but not supported by this hardware. */
HPI_ERROR_UNSUPPORTED_CONTROL_ATTRIBUTE = 406,
/** Control is busy, or coming out of
reset and cannot be accessed at this time. */
HPI_ERROR_CONTROL_NOT_READY = 407,
/** Non volatile memory */
HPI_ERROR_NVMEM_BUSY = 450,
HPI_ERROR_NVMEM_FULL = 451,
HPI_ERROR_NVMEM_FAIL = 452,
/** I2C */
HPI_ERROR_I2C_MISSING_ACK = HPI_ERROR_CONTROL_I2C_MISSING_ACK,
HPI_ERROR_I2C_BAD_ADR = 460,
/** Entity errors */
HPI_ERROR_ENTITY_TYPE_MISMATCH = 470,
HPI_ERROR_ENTITY_ITEM_COUNT = 471,
HPI_ERROR_ENTITY_TYPE_INVALID = 472,
HPI_ERROR_ENTITY_ROLE_INVALID = 473,
/* AES18 specific errors were 500..507 */
/** custom error to use for debugging */
HPI_ERROR_CUSTOM = 600,
/** hpioct32.c can't obtain mutex */
HPI_ERROR_MUTEX_TIMEOUT = 700,
/** errors from HPI backends have values >= this */
HPI_ERROR_BACKEND_BASE = 900,
/** indicates a cached u16 value is invalid. */
HPI_ERROR_ILLEGAL_CACHE_VALUE = 0xffff
};
/** \defgroup maximums HPI maximum values
\{
*/
/** Maximum number of adapters per HPI sub-system
WARNING: modifying this value changes the response structure size.*/
#define HPI_MAX_ADAPTERS 20
/** Maximum number of in or out streams per adapter */
#define HPI_MAX_STREAMS 16
#define HPI_MAX_CHANNELS 2 /* per stream */
#define HPI_MAX_NODES 8 /* per mixer ? */
#define HPI_MAX_CONTROLS 4 /* per node ? */
/** maximum number of ancillary bytes per MPEG frame */
#define HPI_MAX_ANC_BYTES_PER_FRAME (64)
#define HPI_STRING_LEN 16
/** Velocity units */
#define HPI_OSTREAM_VELOCITY_UNITS 4096
/** OutStream timescale units */
#define HPI_OSTREAM_TIMESCALE_UNITS 10000
/** OutStream timescale passthrough - turns timescaling on in passthough mode */
#define HPI_OSTREAM_TIMESCALE_PASSTHROUGH 99999
/**\}*/
/* ////////////////////////////////////////////////////////////////////// */
/* STRUCTURES */
#ifndef DISABLE_PRAGMA_PACK1
#pragma pack(push, 1)
#endif
/** Structure containing sample format information.
See also HPI_FormatCreate().
*/
struct hpi_format {
u32 sample_rate;
/**< 11025, 32000, 44100 ... */
u32 bit_rate; /**< for MPEG */
u32 attributes;
/**< Stereo/JointStereo/Mono */
u16 mode_legacy;
/**< Legacy ancillary mode or idle bit */
u16 unused; /**< unused */
u16 channels; /**< 1,2..., (or ancillary mode or idle bit */
u16 format; /**< HPI_FORMAT_PCM16, _MPEG etc. see #HPI_FORMATS. */
};
struct hpi_anc_frame {
u32 valid_bits_in_this_frame;
u8 b_data[HPI_MAX_ANC_BYTES_PER_FRAME];
};
/** An object for containing a single async event.
*/
struct hpi_async_event {
u16 event_type; /**< type of event. \sa async_event */
u16 sequence; /**< sequence number, allows lost event detection */
u32 state; /**< new state */
u32 h_object; /**< handle to the object returning the event. */
union {
struct {
u16 index; /**< GPIO bit index. */
} gpio;
struct {
u16 node_index; /**< what node is the control on ? */
u16 node_type; /**< what type of node is the control on ? */
} control;
} u;
};
/*/////////////////////////////////////////////////////////////////////////// */
/* Public HPI Entity related definitions */
struct hpi_entity;
enum e_entity_type {
entity_type_null,
entity_type_sequence, /* sequence of potentially heterogeneous TLV entities */
entity_type_reference, /* refers to a TLV entity or NULL */
entity_type_int, /* 32 bit */
entity_type_float, /* ieee754 binary 32 bit encoding */
entity_type_double,
entity_type_cstring,
entity_type_octet,
entity_type_ip4_address,
entity_type_ip6_address,
entity_type_mac_address,
LAST_ENTITY_TYPE
};
enum e_entity_role {
entity_role_null,
entity_role_value,
entity_role_classname,
entity_role_units,
entity_role_flags,
entity_role_range,
entity_role_mapping,
entity_role_enum,
entity_role_instance_of,
entity_role_depends_on,
entity_role_member_of_group,
entity_role_value_constraint,
entity_role_parameter_port,
entity_role_block,
entity_role_node_group,
entity_role_audio_port,
entity_role_clock_port,
LAST_ENTITY_ROLE
};
/* skip host side function declarations for
DSP compile and documentation extraction */
struct hpi_hsubsys {
int not_really_used;
};
#ifndef DISABLE_PRAGMA_PACK1
#pragma pack(pop)
#endif
/*////////////////////////////////////////////////////////////////////////// */
/* HPI FUNCTIONS */
/*/////////////////////////// */
/* DATA and FORMAT and STREAM */
u16 hpi_stream_estimate_buffer_size(struct hpi_format *pF,
u32 host_polling_rate_in_milli_seconds, u32 *recommended_buffer_size);
/*/////////// */
/* SUB SYSTEM */
struct hpi_hsubsys *hpi_subsys_create(void
);
void hpi_subsys_free(const struct hpi_hsubsys *ph_subsys);
u16 hpi_subsys_get_version(const struct hpi_hsubsys *ph_subsys,
u32 *pversion);
u16 hpi_subsys_get_version_ex(const struct hpi_hsubsys *ph_subsys,
u32 *pversion_ex);
u16 hpi_subsys_get_info(const struct hpi_hsubsys *ph_subsys, u32 *pversion,
u16 *pw_num_adapters, u16 aw_adapter_list[], u16 list_length);
u16 hpi_subsys_find_adapters(const struct hpi_hsubsys *ph_subsys,
u16 *pw_num_adapters, u16 aw_adapter_list[], u16 list_length);
u16 hpi_subsys_get_num_adapters(const struct hpi_hsubsys *ph_subsys,
int *pn_num_adapters);
u16 hpi_subsys_get_adapter(const struct hpi_hsubsys *ph_subsys, int iterator,
u32 *padapter_index, u16 *pw_adapter_type);
u16 hpi_subsys_ssx2_bypass(const struct hpi_hsubsys *ph_subsys, u16 bypass);
u16 hpi_subsys_set_host_network_interface(const struct hpi_hsubsys *ph_subsys,
const char *sz_interface);
/*///////// */
/* ADAPTER */
u16 hpi_adapter_open(const struct hpi_hsubsys *ph_subsys, u16 adapter_index);
u16 hpi_adapter_close(const struct hpi_hsubsys *ph_subsys, u16 adapter_index);
u16 hpi_adapter_get_info(const struct hpi_hsubsys *ph_subsys,
u16 adapter_index, u16 *pw_num_outstreams, u16 *pw_num_instreams,
u16 *pw_version, u32 *pserial_number, u16 *pw_adapter_type);
u16 hpi_adapter_get_module_by_index(const struct hpi_hsubsys *ph_subsys,
u16 adapter_index, u16 module_index, u16 *pw_num_outputs,
u16 *pw_num_inputs, u16 *pw_version, u32 *pserial_number,
u16 *pw_module_type, u32 *ph_module);
u16 hpi_adapter_set_mode(const struct hpi_hsubsys *ph_subsys,
u16 adapter_index, u32 adapter_mode);
u16 hpi_adapter_set_mode_ex(const struct hpi_hsubsys *ph_subsys,
u16 adapter_index, u32 adapter_mode, u16 query_or_set);
u16 hpi_adapter_get_mode(const struct hpi_hsubsys *ph_subsys,
u16 adapter_index, u32 *padapter_mode);
u16 hpi_adapter_get_assert(const struct hpi_hsubsys *ph_subsys,
u16 adapter_index, u16 *assert_present, char *psz_assert,
u16 *pw_line_number);
u16 hpi_adapter_get_assert_ex(const struct hpi_hsubsys *ph_subsys,
u16 adapter_index, u16 *assert_present, char *psz_assert,
u32 *pline_number, u16 *pw_assert_on_dsp);
u16 hpi_adapter_test_assert(const struct hpi_hsubsys *ph_subsys,
u16 adapter_index, u16 assert_id);
u16 hpi_adapter_enable_capability(const struct hpi_hsubsys *ph_subsys,
u16 adapter_index, u16 capability, u32 key);
u16 hpi_adapter_self_test(const struct hpi_hsubsys *ph_subsys,
u16 adapter_index);
u16 hpi_adapter_debug_read(const struct hpi_hsubsys *ph_subsys,
u16 adapter_index, u32 dsp_address, char *p_bytes, int *count_bytes);
u16 hpi_adapter_set_property(const struct hpi_hsubsys *ph_subsys,
u16 adapter_index, u16 property, u16 paramter1, u16 paramter2);
u16 hpi_adapter_get_property(const struct hpi_hsubsys *ph_subsys,
u16 adapter_index, u16 property, u16 *pw_paramter1,
u16 *pw_paramter2);
u16 hpi_adapter_enumerate_property(const struct hpi_hsubsys *ph_subsys,
u16 adapter_index, u16 index, u16 what_to_enumerate,
u16 property_index, u32 *psetting);
/*////////////// */
/* NonVol Memory */
u16 hpi_nv_memory_open(const struct hpi_hsubsys *ph_subsys, u16 adapter_index,
u32 *ph_nv_memory, u16 *pw_size_in_bytes);
u16 hpi_nv_memory_read_byte(const struct hpi_hsubsys *ph_subsys,
u32 h_nv_memory, u16 index, u16 *pw_data);
u16 hpi_nv_memory_write_byte(const struct hpi_hsubsys *ph_subsys,
u32 h_nv_memory, u16 index, u16 data);
/*////////////// */
/* Digital I/O */
u16 hpi_gpio_open(const struct hpi_hsubsys *ph_subsys, u16 adapter_index,
u32 *ph_gpio, u16 *pw_number_input_bits, u16 *pw_number_output_bits);
u16 hpi_gpio_read_bit(const struct hpi_hsubsys *ph_subsys, u32 h_gpio,
u16 bit_index, u16 *pw_bit_data);
u16 hpi_gpio_read_all_bits(const struct hpi_hsubsys *ph_subsys, u32 h_gpio,
u16 aw_all_bit_data[4]
);
u16 hpi_gpio_write_bit(const struct hpi_hsubsys *ph_subsys, u32 h_gpio,
u16 bit_index, u16 bit_data);
u16 hpi_gpio_write_status(const struct hpi_hsubsys *ph_subsys, u32 h_gpio,
u16 aw_all_bit_data[4]
);
/**********************/
/* Async Event Object */
/**********************/
u16 hpi_async_event_open(const struct hpi_hsubsys *ph_subsys,
u16 adapter_index, u32 *ph_async);
u16 hpi_async_event_close(const struct hpi_hsubsys *ph_subsys, u32 h_async);
u16 hpi_async_event_wait(const struct hpi_hsubsys *ph_subsys, u32 h_async,
u16 maximum_events, struct hpi_async_event *p_events,
u16 *pw_number_returned);
u16 hpi_async_event_get_count(const struct hpi_hsubsys *ph_subsys,
u32 h_async, u16 *pw_count);
u16 hpi_async_event_get(const struct hpi_hsubsys *ph_subsys, u32 h_async,
u16 maximum_events, struct hpi_async_event *p_events,
u16 *pw_number_returned);
/*/////////// */
/* WATCH-DOG */
u16 hpi_watchdog_open(const struct hpi_hsubsys *ph_subsys, u16 adapter_index,
u32 *ph_watchdog);
u16 hpi_watchdog_set_time(const struct hpi_hsubsys *ph_subsys, u32 h_watchdog,
u32 time_millisec);
u16 hpi_watchdog_ping(const struct hpi_hsubsys *ph_subsys, u32 h_watchdog);
/**************/
/* OUT STREAM */
/**************/
u16 hpi_outstream_open(const struct hpi_hsubsys *ph_subsys, u16 adapter_index,
u16 outstream_index, u32 *ph_outstream);
u16 hpi_outstream_close(const struct hpi_hsubsys *ph_subsys, u32 h_outstream);
u16 hpi_outstream_get_info_ex(const struct hpi_hsubsys *ph_subsys,
u32 h_outstream, u16 *pw_state, u32 *pbuffer_size, u32 *pdata_to_play,
u32 *psamples_played, u32 *pauxiliary_data_to_play);
u16 hpi_outstream_write_buf(const struct hpi_hsubsys *ph_subsys,
u32 h_outstream, const u8 *pb_write_buf, u32 bytes_to_write,
const struct hpi_format *p_format);
u16 hpi_outstream_start(const struct hpi_hsubsys *ph_subsys, u32 h_outstream);
u16 hpi_outstream_wait_start(const struct hpi_hsubsys *ph_subsys,
u32 h_outstream);
u16 hpi_outstream_stop(const struct hpi_hsubsys *ph_subsys, u32 h_outstream);
u16 hpi_outstream_sinegen(const struct hpi_hsubsys *ph_subsys,
u32 h_outstream);
u16 hpi_outstream_reset(const struct hpi_hsubsys *ph_subsys, u32 h_outstream);
u16 hpi_outstream_query_format(const struct hpi_hsubsys *ph_subsys,
u32 h_outstream, struct hpi_format *p_format);
u16 hpi_outstream_set_format(const struct hpi_hsubsys *ph_subsys,
u32 h_outstream, struct hpi_format *p_format);
u16 hpi_outstream_set_punch_in_out(const struct hpi_hsubsys *ph_subsys,
u32 h_outstream, u32 punch_in_sample, u32 punch_out_sample);
u16 hpi_outstream_set_velocity(const struct hpi_hsubsys *ph_subsys,
u32 h_outstream, short velocity);
u16 hpi_outstream_ancillary_reset(const struct hpi_hsubsys *ph_subsys,
u32 h_outstream, u16 mode);
u16 hpi_outstream_ancillary_get_info(const struct hpi_hsubsys *ph_subsys,
u32 h_outstream, u32 *pframes_available);
u16 hpi_outstream_ancillary_read(const struct hpi_hsubsys *ph_subsys,
u32 h_outstream, struct hpi_anc_frame *p_anc_frame_buffer,
u32 anc_frame_buffer_size_in_bytes,
u32 number_of_ancillary_frames_to_read);
u16 hpi_outstream_set_time_scale(const struct hpi_hsubsys *ph_subsys,
u32 h_outstream, u32 time_scaleX10000);
u16 hpi_outstream_host_buffer_allocate(const struct hpi_hsubsys *ph_subsys,
u32 h_outstream, u32 size_in_bytes);
u16 hpi_outstream_host_buffer_free(const struct hpi_hsubsys *ph_subsys,
u32 h_outstream);
u16 hpi_outstream_group_add(const struct hpi_hsubsys *ph_subsys,
u32 h_outstream, u32 h_stream);
u16 hpi_outstream_group_get_map(const struct hpi_hsubsys *ph_subsys,
u32 h_outstream, u32 *poutstream_map, u32 *pinstream_map);
u16 hpi_outstream_group_reset(const struct hpi_hsubsys *ph_subsys,
u32 h_outstream);
/*////////// */
/* IN_STREAM */
u16 hpi_instream_open(const struct hpi_hsubsys *ph_subsys, u16 adapter_index,
u16 instream_index, u32 *ph_instream);
u16 hpi_instream_close(const struct hpi_hsubsys *ph_subsys, u32 h_instream);
u16 hpi_instream_query_format(const struct hpi_hsubsys *ph_subsys,
u32 h_instream, const struct hpi_format *p_format);
u16 hpi_instream_set_format(const struct hpi_hsubsys *ph_subsys,
u32 h_instream, const struct hpi_format *p_format);
u16 hpi_instream_read_buf(const struct hpi_hsubsys *ph_subsys, u32 h_instream,
u8 *pb_read_buf, u32 bytes_to_read);
u16 hpi_instream_start(const struct hpi_hsubsys *ph_subsys, u32 h_instream);
u16 hpi_instream_wait_start(const struct hpi_hsubsys *ph_subsys,
u32 h_instream);
u16 hpi_instream_stop(const struct hpi_hsubsys *ph_subsys, u32 h_instream);
u16 hpi_instream_reset(const struct hpi_hsubsys *ph_subsys, u32 h_instream);
u16 hpi_instream_get_info_ex(const struct hpi_hsubsys *ph_subsys,
u32 h_instream, u16 *pw_state, u32 *pbuffer_size, u32 *pdata_recorded,
u32 *psamples_recorded, u32 *pauxiliary_data_recorded);
u16 hpi_instream_ancillary_reset(const struct hpi_hsubsys *ph_subsys,
u32 h_instream, u16 bytes_per_frame, u16 mode, u16 alignment,
u16 idle_bit);
u16 hpi_instream_ancillary_get_info(const struct hpi_hsubsys *ph_subsys,
u32 h_instream, u32 *pframe_space);
u16 hpi_instream_ancillary_write(const struct hpi_hsubsys *ph_subsys,
u32 h_instream, const struct hpi_anc_frame *p_anc_frame_buffer,
u32 anc_frame_buffer_size_in_bytes,
u32 number_of_ancillary_frames_to_write);
u16 hpi_instream_host_buffer_allocate(const struct hpi_hsubsys *ph_subsys,
u32 h_instream, u32 size_in_bytes);
u16 hpi_instream_host_buffer_free(const struct hpi_hsubsys *ph_subsys,
u32 h_instream);
u16 hpi_instream_group_add(const struct hpi_hsubsys *ph_subsys,
u32 h_instream, u32 h_stream);
u16 hpi_instream_group_get_map(const struct hpi_hsubsys *ph_subsys,
u32 h_instream, u32 *poutstream_map, u32 *pinstream_map);
u16 hpi_instream_group_reset(const struct hpi_hsubsys *ph_subsys,
u32 h_instream);
/*********/
/* MIXER */
/*********/
u16 hpi_mixer_open(const struct hpi_hsubsys *ph_subsys, u16 adapter_index,
u32 *ph_mixer);
u16 hpi_mixer_close(const struct hpi_hsubsys *ph_subsys, u32 h_mixer);
u16 hpi_mixer_get_control(const struct hpi_hsubsys *ph_subsys, u32 h_mixer,
u16 src_node_type, u16 src_node_type_index, u16 dst_node_type,
u16 dst_node_type_index, u16 control_type, u32 *ph_control);
u16 hpi_mixer_get_control_by_index(const struct hpi_hsubsys *ph_subsys,
u32 h_mixer, u16 control_index, u16 *pw_src_node_type,
u16 *pw_src_node_index, u16 *pw_dst_node_type, u16 *pw_dst_node_index,
u16 *pw_control_type, u32 *ph_control);
u16 hpi_mixer_store(const struct hpi_hsubsys *ph_subsys, u32 h_mixer,
enum HPI_MIXER_STORE_COMMAND command, u16 index);
/*************************/
/* mixer CONTROLS */
/*************************/
/*************************/
/* volume control */
/*************************/
u16 hpi_volume_set_gain(const struct hpi_hsubsys *ph_subsys, u32 h_control,
short an_gain0_01dB[HPI_MAX_CHANNELS]
);
u16 hpi_volume_get_gain(const struct hpi_hsubsys *ph_subsys, u32 h_control,
short an_gain0_01dB_out[HPI_MAX_CHANNELS]
);
#define hpi_volume_get_range hpi_volume_query_range
u16 hpi_volume_query_range(const struct hpi_hsubsys *ph_subsys, u32 h_control,
short *min_gain_01dB, short *max_gain_01dB, short *step_gain_01dB);
u16 hpi_volume_query_channels(const struct hpi_hsubsys *ph_subsys,
const u32 h_volume, u32 *p_channels);
u16 hpi_volume_auto_fade(const struct hpi_hsubsys *ph_subsys, u32 h_control,
short an_stop_gain0_01dB[HPI_MAX_CHANNELS], u32 duration_ms);
u16 hpi_volume_auto_fade_profile(const struct hpi_hsubsys *ph_subsys,
u32 h_control, short an_stop_gain0_01dB[HPI_MAX_CHANNELS],
u32 duration_ms, u16 profile);
/*************************/
/* level control */
/*************************/
u16 hpi_level_query_range(const struct hpi_hsubsys *ph_subsys, u32 h_control,
short *min_gain_01dB, short *max_gain_01dB, short *step_gain_01dB);
u16 hpi_level_set_gain(const struct hpi_hsubsys *ph_subsys, u32 h_control,
short an_gain0_01dB[HPI_MAX_CHANNELS]
);
u16 hpi_level_get_gain(const struct hpi_hsubsys *ph_subsys, u32 h_control,
short an_gain0_01dB_out[HPI_MAX_CHANNELS]
);
/*************************/
/* meter control */
/*************************/
u16 hpi_meter_query_channels(const struct hpi_hsubsys *ph_subsys,
const u32 h_meter, u32 *p_channels);
u16 hpi_meter_get_peak(const struct hpi_hsubsys *ph_subsys, u32 h_control,
short an_peak0_01dB_out[HPI_MAX_CHANNELS]
);
u16 hpi_meter_get_rms(const struct hpi_hsubsys *ph_subsys, u32 h_control,
short an_peak0_01dB_out[HPI_MAX_CHANNELS]
);
u16 hpi_meter_set_peak_ballistics(const struct hpi_hsubsys *ph_subsys,
u32 h_control, u16 attack, u16 decay);
u16 hpi_meter_set_rms_ballistics(const struct hpi_hsubsys *ph_subsys,
u32 h_control, u16 attack, u16 decay);
u16 hpi_meter_get_peak_ballistics(const struct hpi_hsubsys *ph_subsys,
u32 h_control, u16 *attack, u16 *decay);
u16 hpi_meter_get_rms_ballistics(const struct hpi_hsubsys *ph_subsys,
u32 h_control, u16 *attack, u16 *decay);
/*************************/
/* channel mode control */
/*************************/
u16 hpi_channel_mode_query_mode(const struct hpi_hsubsys *ph_subsys,
const u32 h_mode, const u32 index, u16 *pw_mode);
u16 hpi_channel_mode_set(const struct hpi_hsubsys *ph_subsys, u32 h_control,
u16 mode);
u16 hpi_channel_mode_get(const struct hpi_hsubsys *ph_subsys, u32 h_control,
u16 *mode);
/*************************/
/* Tuner control */
/*************************/
u16 hpi_tuner_query_band(const struct hpi_hsubsys *ph_subsys,
const u32 h_tuner, const u32 index, u16 *pw_band);
u16 hpi_tuner_set_band(const struct hpi_hsubsys *ph_subsys, u32 h_control,
u16 band);
u16 hpi_tuner_get_band(const struct hpi_hsubsys *ph_subsys, u32 h_control,
u16 *pw_band);
u16 hpi_tuner_query_frequency(const struct hpi_hsubsys *ph_subsys,
const u32 h_tuner, const u32 index, const u16 band, u32 *pfreq);
u16 hpi_tuner_set_frequency(const struct hpi_hsubsys *ph_subsys,
u32 h_control, u32 freq_ink_hz);
u16 hpi_tuner_get_frequency(const struct hpi_hsubsys *ph_subsys,
u32 h_control, u32 *pw_freq_ink_hz);
u16 hpi_tuner_getRF_level(const struct hpi_hsubsys *ph_subsys, u32 h_control,
short *pw_level);
u16 hpi_tuner_get_rawRF_level(const struct hpi_hsubsys *ph_subsys,
u32 h_control, short *pw_level);
u16 hpi_tuner_query_gain(const struct hpi_hsubsys *ph_subsys,
const u32 h_tuner, const u32 index, u16 *pw_gain);
u16 hpi_tuner_set_gain(const struct hpi_hsubsys *ph_subsys, u32 h_control,
short gain);
u16 hpi_tuner_get_gain(const struct hpi_hsubsys *ph_subsys, u32 h_control,
short *pn_gain);
u16 hpi_tuner_get_status(const struct hpi_hsubsys *ph_subsys, u32 h_control,
u16 *pw_status_mask, u16 *pw_status);
u16 hpi_tuner_set_mode(const struct hpi_hsubsys *ph_subsys, u32 h_control,
u32 mode, u32 value);
u16 hpi_tuner_get_mode(const struct hpi_hsubsys *ph_subsys, u32 h_control,
u32 mode, u32 *pn_value);
u16 hpi_tuner_getRDS(const struct hpi_hsubsys *ph_subsys, u32 h_control,
char *p_rds_data);
u16 hpi_tuner_query_deemphasis(const struct hpi_hsubsys *ph_subsys,
const u32 h_tuner, const u32 index, const u16 band, u32 *pdeemphasis);
u16 hpi_tuner_set_deemphasis(const struct hpi_hsubsys *ph_subsys,
u32 h_control, u32 deemphasis);
u16 hpi_tuner_get_deemphasis(const struct hpi_hsubsys *ph_subsys,
u32 h_control, u32 *pdeemphasis);
u16 hpi_tuner_query_program(const struct hpi_hsubsys *ph_subsys,
const u32 h_tuner, u32 *pbitmap_program);
u16 hpi_tuner_set_program(const struct hpi_hsubsys *ph_subsys, u32 h_control,
u32 program);
u16 hpi_tuner_get_program(const struct hpi_hsubsys *ph_subsys, u32 h_control,
u32 *pprogram);
u16 hpi_tuner_get_hd_radio_dsp_version(const struct hpi_hsubsys *ph_subsys,
u32 h_control, char *psz_dsp_version, const u32 string_size);
u16 hpi_tuner_get_hd_radio_sdk_version(const struct hpi_hsubsys *ph_subsys,
u32 h_control, char *psz_sdk_version, const u32 string_size);
u16 hpi_tuner_get_hd_radio_signal_quality(const struct hpi_hsubsys *ph_subsys,
u32 h_control, u32 *pquality);
/****************************/
/* PADs control */
/****************************/
u16 HPI_PAD__get_channel_name(const struct hpi_hsubsys *ph_subsys,
u32 h_control, char *psz_string, const u32 string_length);
u16 HPI_PAD__get_artist(const struct hpi_hsubsys *ph_subsys, u32 h_control,
char *psz_string, const u32 string_length);
u16 HPI_PAD__get_title(const struct hpi_hsubsys *ph_subsys, u32 h_control,
char *psz_string, const u32 string_length);
u16 HPI_PAD__get_comment(const struct hpi_hsubsys *ph_subsys, u32 h_control,
char *psz_string, const u32 string_length);
u16 HPI_PAD__get_program_type(const struct hpi_hsubsys *ph_subsys,
u32 h_control, u32 *ppTY);
u16 HPI_PAD__get_rdsPI(const struct hpi_hsubsys *ph_subsys, u32 h_control,
u32 *ppI);
u16 HPI_PAD__get_program_type_string(const struct hpi_hsubsys *ph_subsys,
u32 h_control, const u32 data_type, const u32 pTY, char *psz_string,
const u32 string_length);
/****************************/
/* AES/EBU Receiver control */
/****************************/
u16 HPI_AESEBU__receiver_query_format(const struct hpi_hsubsys *ph_subsys,
const u32 h_aes_rx, const u32 index, u16 *pw_format);
u16 HPI_AESEBU__receiver_set_format(const struct hpi_hsubsys *ph_subsys,
u32 h_control, u16 source);
u16 HPI_AESEBU__receiver_get_format(const struct hpi_hsubsys *ph_subsys,
u32 h_control, u16 *pw_source);
u16 HPI_AESEBU__receiver_get_sample_rate(const struct hpi_hsubsys *ph_subsys,
u32 h_control, u32 *psample_rate);
u16 HPI_AESEBU__receiver_get_user_data(const struct hpi_hsubsys *ph_subsys,
u32 h_control, u16 index, u16 *pw_data);
u16 HPI_AESEBU__receiver_get_channel_status(const struct hpi_hsubsys
*ph_subsys, u32 h_control, u16 index, u16 *pw_data);
u16 HPI_AESEBU__receiver_get_error_status(const struct hpi_hsubsys *ph_subsys,
u32 h_control, u16 *pw_error_data);
/*******************************/
/* AES/EBU Transmitter control */
/*******************************/
u16 HPI_AESEBU__transmitter_set_sample_rate(const struct hpi_hsubsys
*ph_subsys, u32 h_control, u32 sample_rate);
u16 HPI_AESEBU__transmitter_set_user_data(const struct hpi_hsubsys *ph_subsys,
u32 h_control, u16 index, u16 data);
u16 HPI_AESEBU__transmitter_set_channel_status(const struct hpi_hsubsys
*ph_subsys, u32 h_control, u16 index, u16 data);
u16 HPI_AESEBU__transmitter_get_channel_status(const struct hpi_hsubsys
*ph_subsys, u32 h_control, u16 index, u16 *pw_data);
u16 HPI_AESEBU__transmitter_query_format(const struct hpi_hsubsys *ph_subsys,
const u32 h_aes_tx, const u32 index, u16 *pw_format);
u16 HPI_AESEBU__transmitter_set_format(const struct hpi_hsubsys *ph_subsys,
u32 h_control, u16 output_format);
u16 HPI_AESEBU__transmitter_get_format(const struct hpi_hsubsys *ph_subsys,
u32 h_control, u16 *pw_output_format);
/***********************/
/* multiplexer control */
/***********************/
u16 hpi_multiplexer_set_source(const struct hpi_hsubsys *ph_subsys,
u32 h_control, u16 source_node_type, u16 source_node_index);
u16 hpi_multiplexer_get_source(const struct hpi_hsubsys *ph_subsys,
u32 h_control, u16 *source_node_type, u16 *source_node_index);
u16 hpi_multiplexer_query_source(const struct hpi_hsubsys *ph_subsys,
u32 h_control, u16 index, u16 *source_node_type,
u16 *source_node_index);
/***************/
/* VOX control */
/***************/
u16 hpi_vox_set_threshold(const struct hpi_hsubsys *ph_subsys, u32 h_control,
short an_gain0_01dB);
u16 hpi_vox_get_threshold(const struct hpi_hsubsys *ph_subsys, u32 h_control,
short *an_gain0_01dB);
/*********************/
/* Bitstream control */
/*********************/
u16 hpi_bitstream_set_clock_edge(const struct hpi_hsubsys *ph_subsys,
u32 h_control, u16 edge_type);
u16 hpi_bitstream_set_data_polarity(const struct hpi_hsubsys *ph_subsys,
u32 h_control, u16 polarity);
u16 hpi_bitstream_get_activity(const struct hpi_hsubsys *ph_subsys,
u32 h_control, u16 *pw_clk_activity, u16 *pw_data_activity);
/***********************/
/* SampleClock control */
/***********************/
u16 hpi_sample_clock_query_source(const struct hpi_hsubsys *ph_subsys,
const u32 h_clock, const u32 index, u16 *pw_source);
u16 hpi_sample_clock_set_source(const struct hpi_hsubsys *ph_subsys,
u32 h_control, u16 source);
u16 hpi_sample_clock_get_source(const struct hpi_hsubsys *ph_subsys,
u32 h_control, u16 *pw_source);
u16 hpi_sample_clock_query_source_index(const struct hpi_hsubsys *ph_subsys,
const u32 h_clock, const u32 index, const u32 source,
u16 *pw_source_index);
u16 hpi_sample_clock_set_source_index(const struct hpi_hsubsys *ph_subsys,
u32 h_control, u16 source_index);
u16 hpi_sample_clock_get_source_index(const struct hpi_hsubsys *ph_subsys,
u32 h_control, u16 *pw_source_index);
u16 hpi_sample_clock_get_sample_rate(const struct hpi_hsubsys *ph_subsys,
u32 h_control, u32 *psample_rate);
u16 hpi_sample_clock_query_local_rate(const struct hpi_hsubsys *ph_subsys,
const u32 h_clock, const u32 index, u32 *psource);
u16 hpi_sample_clock_set_local_rate(const struct hpi_hsubsys *ph_subsys,
u32 h_control, u32 sample_rate);
u16 hpi_sample_clock_get_local_rate(const struct hpi_hsubsys *ph_subsys,
u32 h_control, u32 *psample_rate);
u16 hpi_sample_clock_set_auto(const struct hpi_hsubsys *ph_subsys,
u32 h_control, u32 enable);
u16 hpi_sample_clock_get_auto(const struct hpi_hsubsys *ph_subsys,
u32 h_control, u32 *penable);
u16 hpi_sample_clock_set_local_rate_lock(const struct hpi_hsubsys *ph_subsys,
u32 h_control, u32 lock);
u16 hpi_sample_clock_get_local_rate_lock(const struct hpi_hsubsys *ph_subsys,
u32 h_control, u32 *plock);
/***********************/
/* Microphone control */
/***********************/
u16 hpi_microphone_set_phantom_power(const struct hpi_hsubsys *ph_subsys,
u32 h_control, u16 on_off);
u16 hpi_microphone_get_phantom_power(const struct hpi_hsubsys *ph_subsys,
u32 h_control, u16 *pw_on_off);
/*******************************
Parametric Equalizer control
*******************************/
u16 hpi_parametricEQ__get_info(const struct hpi_hsubsys *ph_subsys,
u32 h_control, u16 *pw_number_of_bands, u16 *pw_enabled);
u16 hpi_parametricEQ__set_state(const struct hpi_hsubsys *ph_subsys,
u32 h_control, u16 on_off);
u16 hpi_parametricEQ__set_band(const struct hpi_hsubsys *ph_subsys,
u32 h_control, u16 index, u16 type, u32 frequency_hz, short q100,
short gain0_01dB);
u16 hpi_parametricEQ__get_band(const struct hpi_hsubsys *ph_subsys,
u32 h_control, u16 index, u16 *pn_type, u32 *pfrequency_hz,
short *pnQ100, short *pn_gain0_01dB);
u16 hpi_parametricEQ__get_coeffs(const struct hpi_hsubsys *ph_subsys,
u32 h_control, u16 index, short coeffs[5]
);
/*******************************
Compressor Expander control
*******************************/
u16 hpi_compander_set(const struct hpi_hsubsys *ph_subsys, u32 h_control,
u16 attack, u16 decay, short ratio100, short threshold0_01dB,
short makeup_gain0_01dB);
u16 hpi_compander_get(const struct hpi_hsubsys *ph_subsys, u32 h_control,
u16 *pw_attack, u16 *pw_decay, short *pw_ratio100,
short *pn_threshold0_01dB, short *pn_makeup_gain0_01dB);
/*******************************
Cobranet HMI control
*******************************/
u16 hpi_cobranet_hmi_write(const struct hpi_hsubsys *ph_subsys, u32 h_control,
u32 hmi_address, u32 byte_count, u8 *pb_data);
u16 hpi_cobranet_hmi_read(const struct hpi_hsubsys *ph_subsys, u32 h_control,
u32 hmi_address, u32 max_byte_count, u32 *pbyte_count, u8 *pb_data);
u16 hpi_cobranet_hmi_get_status(const struct hpi_hsubsys *ph_subsys,
u32 h_control, u32 *pstatus, u32 *preadable_size,
u32 *pwriteable_size);
/*Read the current IP address
*/
u16 hpi_cobranet_getI_paddress(const struct hpi_hsubsys *ph_subsys,
u32 h_control, u32 *pi_paddress);
/* Write the current IP address
*/
u16 hpi_cobranet_setI_paddress(const struct hpi_hsubsys *ph_subsys,
u32 h_control, u32 i_paddress);
/* Read the static IP address
*/
u16 hpi_cobranet_get_staticI_paddress(const struct hpi_hsubsys *ph_subsys,
u32 h_control, u32 *pi_paddress);
/* Write the static IP address
*/
u16 hpi_cobranet_set_staticI_paddress(const struct hpi_hsubsys *ph_subsys,
u32 h_control, u32 i_paddress);
/* Read the MAC address
*/
u16 hpi_cobranet_getMA_caddress(const struct hpi_hsubsys *ph_subsys,
u32 h_control, u32 *pmAC_MS_bs, u32 *pmAC_LS_bs);
/*******************************
Tone Detector control
*******************************/
u16 hpi_tone_detector_get_state(const struct hpi_hsubsys *ph_subsys, u32 hC,
u32 *state);
u16 hpi_tone_detector_set_enable(const struct hpi_hsubsys *ph_subsys, u32 hC,
u32 enable);
u16 hpi_tone_detector_get_enable(const struct hpi_hsubsys *ph_subsys, u32 hC,
u32 *enable);
u16 hpi_tone_detector_set_event_enable(const struct hpi_hsubsys *ph_subsys,
u32 hC, u32 event_enable);
u16 hpi_tone_detector_get_event_enable(const struct hpi_hsubsys *ph_subsys,
u32 hC, u32 *event_enable);
u16 hpi_tone_detector_set_threshold(const struct hpi_hsubsys *ph_subsys,
u32 hC, int threshold);
u16 hpi_tone_detector_get_threshold(const struct hpi_hsubsys *ph_subsys,
u32 hC, int *threshold);
u16 hpi_tone_detector_get_frequency(const struct hpi_hsubsys *ph_subsys,
u32 hC, u32 index, u32 *frequency);
/*******************************
Silence Detector control
*******************************/
u16 hpi_silence_detector_get_state(const struct hpi_hsubsys *ph_subsys,
u32 hC, u32 *state);
u16 hpi_silence_detector_set_enable(const struct hpi_hsubsys *ph_subsys,
u32 hC, u32 enable);
u16 hpi_silence_detector_get_enable(const struct hpi_hsubsys *ph_subsys,
u32 hC, u32 *enable);
u16 hpi_silence_detector_set_event_enable(const struct hpi_hsubsys *ph_subsys,
u32 hC, u32 event_enable);
u16 hpi_silence_detector_get_event_enable(const struct hpi_hsubsys *ph_subsys,
u32 hC, u32 *event_enable);
u16 hpi_silence_detector_set_delay(const struct hpi_hsubsys *ph_subsys,
u32 hC, u32 delay);
u16 hpi_silence_detector_get_delay(const struct hpi_hsubsys *ph_subsys,
u32 hC, u32 *delay);
u16 hpi_silence_detector_set_threshold(const struct hpi_hsubsys *ph_subsys,
u32 hC, int threshold);
u16 hpi_silence_detector_get_threshold(const struct hpi_hsubsys *ph_subsys,
u32 hC, int *threshold);
/*******************************
Universal control
*******************************/
u16 hpi_entity_find_next(struct hpi_entity *container_entity,
enum e_entity_type type, enum e_entity_role role, int recursive_flag,
struct hpi_entity **current_match);
u16 hpi_entity_copy_value_from(struct hpi_entity *entity,
enum e_entity_type type, size_t item_count, void *value_dst_p);
u16 hpi_entity_unpack(struct hpi_entity *entity, enum e_entity_type *type,
size_t *items, enum e_entity_role *role, void **value);
u16 hpi_entity_alloc_and_pack(const enum e_entity_type type,
const size_t item_count, const enum e_entity_role role, void *value,
struct hpi_entity **entity);
void hpi_entity_free(struct hpi_entity *entity);
u16 hpi_universal_info(const struct hpi_hsubsys *ph_subsys, u32 hC,
struct hpi_entity **info);
u16 hpi_universal_get(const struct hpi_hsubsys *ph_subsys, u32 hC,
struct hpi_entity **value);
u16 hpi_universal_set(const struct hpi_hsubsys *ph_subsys, u32 hC,
struct hpi_entity *value);
/*/////////// */
/* DSP CLOCK */
/*/////////// */
u16 hpi_clock_open(const struct hpi_hsubsys *ph_subsys, u16 adapter_index,
u32 *ph_dsp_clock);
u16 hpi_clock_set_time(const struct hpi_hsubsys *ph_subsys, u32 h_clock,
u16 hour, u16 minute, u16 second, u16 milli_second);
u16 hpi_clock_get_time(const struct hpi_hsubsys *ph_subsys, u32 h_clock,
u16 *pw_hour, u16 *pw_minute, u16 *pw_second, u16 *pw_milli_second);
/*/////////// */
/* PROFILE */
/*/////////// */
u16 hpi_profile_open_all(const struct hpi_hsubsys *ph_subsys,
u16 adapter_index, u16 profile_index, u32 *ph_profile,
u16 *pw_max_profiles);
u16 hpi_profile_get(const struct hpi_hsubsys *ph_subsys, u32 h_profile,
u16 index, u16 *pw_seconds, u32 *pmicro_seconds, u32 *pcall_count,
u32 *pmax_micro_seconds, u32 *pmin_micro_seconds);
u16 hpi_profile_start_all(const struct hpi_hsubsys *ph_subsys, u32 h_profile);
u16 hpi_profile_stop_all(const struct hpi_hsubsys *ph_subsys, u32 h_profile);
u16 hpi_profile_get_name(const struct hpi_hsubsys *ph_subsys, u32 h_profile,
u16 index, char *sz_profile_name, u16 profile_name_length);
u16 hpi_profile_get_utilization(const struct hpi_hsubsys *ph_subsys,
u32 h_profile, u32 *putilization);
/*//////////////////// */
/* UTILITY functions */
u16 hpi_format_create(struct hpi_format *p_format, u16 channels, u16 format,
u32 sample_rate, u32 bit_rate, u32 attributes);
/* Until it's verified, this function is for Windows OSs only */
#endif /*_H_HPI_ */
/*
///////////////////////////////////////////////////////////////////////////////
// See CVS for history. Last complete set in rev 1.146
////////////////////////////////////////////////////////////////////////////////
*/
/******************************************************************************
AudioScience HPI driver
Copyright (C) 1997-2010 AudioScience Inc. <support@audioscience.com>
This program is free software; you can redistribute it and/or modify
it under the terms of version 2 of the GNU General Public License as
published by the Free Software Foundation;
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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
Hardware Programming Interface (HPI) for AudioScience ASI6200 series adapters.
These PCI bus adapters are based on the TI C6711 DSP.
Exported functions:
void HPI_6000(struct hpi_message *phm, struct hpi_response *phr)
#defines
HIDE_PCI_ASSERTS to show the PCI asserts
PROFILE_DSP2 get profile data from DSP2 if present (instead of DSP 1)
(C) Copyright AudioScience Inc. 1998-2003
*******************************************************************************/
#define SOURCEFILE_NAME "hpi6000.c"
#include "hpi_internal.h"
#include "hpimsginit.h"
#include "hpidebug.h"
#include "hpi6000.h"
#include "hpidspcd.h"
#include "hpicmn.h"
#define HPI_HIF_BASE (0x00000200) /* start of C67xx internal RAM */
#define HPI_HIF_ADDR(member) \
(HPI_HIF_BASE + offsetof(struct hpi_hif_6000, member))
#define HPI_HIF_ERROR_MASK 0x4000
/* HPI6000 specific error codes */
#define HPI6000_ERROR_BASE 900
#define HPI6000_ERROR_MSG_RESP_IDLE_TIMEOUT 901
#define HPI6000_ERROR_MSG_RESP_SEND_MSG_ACK 902
#define HPI6000_ERROR_MSG_RESP_GET_RESP_ACK 903
#define HPI6000_ERROR_MSG_GET_ADR 904
#define HPI6000_ERROR_RESP_GET_ADR 905
#define HPI6000_ERROR_MSG_RESP_BLOCKWRITE32 906
#define HPI6000_ERROR_MSG_RESP_BLOCKREAD32 907
#define HPI6000_ERROR_MSG_INVALID_DSP_INDEX 908
#define HPI6000_ERROR_CONTROL_CACHE_PARAMS 909
#define HPI6000_ERROR_SEND_DATA_IDLE_TIMEOUT 911
#define HPI6000_ERROR_SEND_DATA_ACK 912
#define HPI6000_ERROR_SEND_DATA_ADR 913
#define HPI6000_ERROR_SEND_DATA_TIMEOUT 914
#define HPI6000_ERROR_SEND_DATA_CMD 915
#define HPI6000_ERROR_SEND_DATA_WRITE 916
#define HPI6000_ERROR_SEND_DATA_IDLECMD 917
#define HPI6000_ERROR_SEND_DATA_VERIFY 918
#define HPI6000_ERROR_GET_DATA_IDLE_TIMEOUT 921
#define HPI6000_ERROR_GET_DATA_ACK 922
#define HPI6000_ERROR_GET_DATA_CMD 923
#define HPI6000_ERROR_GET_DATA_READ 924
#define HPI6000_ERROR_GET_DATA_IDLECMD 925
#define HPI6000_ERROR_CONTROL_CACHE_ADDRLEN 951
#define HPI6000_ERROR_CONTROL_CACHE_READ 952
#define HPI6000_ERROR_CONTROL_CACHE_FLUSH 953
#define HPI6000_ERROR_MSG_RESP_GETRESPCMD 961
#define HPI6000_ERROR_MSG_RESP_IDLECMD 962
#define HPI6000_ERROR_MSG_RESP_BLOCKVERIFY32 963
/* adapter init errors */
#define HPI6000_ERROR_UNHANDLED_SUBSYS_ID 930
/* can't access PCI2040 */
#define HPI6000_ERROR_INIT_PCI2040 931
/* can't access DSP HPI i/f */
#define HPI6000_ERROR_INIT_DSPHPI 932
/* can't access internal DSP memory */
#define HPI6000_ERROR_INIT_DSPINTMEM 933
/* can't access SDRAM - test#1 */
#define HPI6000_ERROR_INIT_SDRAM1 934
/* can't access SDRAM - test#2 */
#define HPI6000_ERROR_INIT_SDRAM2 935
#define HPI6000_ERROR_INIT_VERIFY 938
#define HPI6000_ERROR_INIT_NOACK 939
#define HPI6000_ERROR_INIT_PLDTEST1 941
#define HPI6000_ERROR_INIT_PLDTEST2 942
/* local defines */
#define HIDE_PCI_ASSERTS
#define PROFILE_DSP2
/* for PCI2040 i/f chip */
/* HPI CSR registers */
/* word offsets from CSR base */
/* use when io addresses defined as u32 * */
#define INTERRUPT_EVENT_SET 0
#define INTERRUPT_EVENT_CLEAR 1
#define INTERRUPT_MASK_SET 2
#define INTERRUPT_MASK_CLEAR 3
#define HPI_ERROR_REPORT 4
#define HPI_RESET 5
#define HPI_DATA_WIDTH 6
#define MAX_DSPS 2
/* HPI registers, spaced 8K bytes = 2K words apart */
#define DSP_SPACING 0x800
#define CONTROL 0x0000
#define ADDRESS 0x0200
#define DATA_AUTOINC 0x0400
#define DATA 0x0600
#define TIMEOUT 500000
struct dsp_obj {
__iomem u32 *prHPI_control;
__iomem u32 *prHPI_address;
__iomem u32 *prHPI_data;
__iomem u32 *prHPI_data_auto_inc;
char c_dsp_rev; /*A, B */
u32 control_cache_address_on_dsp;
u32 control_cache_length_on_dsp;
struct hpi_adapter_obj *pa_parent_adapter;
};
struct hpi_hw_obj {
__iomem u32 *dw2040_HPICSR;
__iomem u32 *dw2040_HPIDSP;
u16 num_dsp;
struct dsp_obj ado[MAX_DSPS];
u32 message_buffer_address_on_dsp;
u32 response_buffer_address_on_dsp;
u32 pCI2040HPI_error_count;
struct hpi_control_cache_single control_cache[HPI_NMIXER_CONTROLS];
struct hpi_control_cache *p_cache;
};
static u16 hpi6000_dsp_block_write32(struct hpi_adapter_obj *pao,
u16 dsp_index, u32 hpi_address, u32 *source, u32 count);
static u16 hpi6000_dsp_block_read32(struct hpi_adapter_obj *pao,
u16 dsp_index, u32 hpi_address, u32 *dest, u32 count);
static short hpi6000_adapter_boot_load_dsp(struct hpi_adapter_obj *pao,
u32 *pos_error_code);
static short hpi6000_check_PCI2040_error_flag(struct hpi_adapter_obj *pao,
u16 read_or_write);
#define H6READ 1
#define H6WRITE 0
static short hpi6000_update_control_cache(struct hpi_adapter_obj *pao,
struct hpi_message *phm);
static short hpi6000_message_response_sequence(struct hpi_adapter_obj *pao,
u16 dsp_index, struct hpi_message *phm, struct hpi_response *phr);
static void hw_message(struct hpi_adapter_obj *pao, struct hpi_message *phm,
struct hpi_response *phr);
static short hpi6000_wait_dsp_ack(struct hpi_adapter_obj *pao, u16 dsp_index,
u32 ack_value);
static short hpi6000_send_host_command(struct hpi_adapter_obj *pao,
u16 dsp_index, u32 host_cmd);
static void hpi6000_send_dsp_interrupt(struct dsp_obj *pdo);
static short hpi6000_send_data(struct hpi_adapter_obj *pao, u16 dsp_index,
struct hpi_message *phm, struct hpi_response *phr);
static short hpi6000_get_data(struct hpi_adapter_obj *pao, u16 dsp_index,
struct hpi_message *phm, struct hpi_response *phr);
static void hpi_write_word(struct dsp_obj *pdo, u32 address, u32 data);
static u32 hpi_read_word(struct dsp_obj *pdo, u32 address);
static void hpi_write_block(struct dsp_obj *pdo, u32 address, u32 *pdata,
u32 length);
static void hpi_read_block(struct dsp_obj *pdo, u32 address, u32 *pdata,
u32 length);
static void subsys_create_adapter(struct hpi_message *phm,
struct hpi_response *phr);
static void subsys_delete_adapter(struct hpi_message *phm,
struct hpi_response *phr);
static void adapter_get_asserts(struct hpi_adapter_obj *pao,
struct hpi_message *phm, struct hpi_response *phr);
static short create_adapter_obj(struct hpi_adapter_obj *pao,
u32 *pos_error_code);
/* local globals */
static u16 gw_pci_read_asserts; /* used to count PCI2040 errors */
static u16 gw_pci_write_asserts; /* used to count PCI2040 errors */
static void subsys_message(struct hpi_message *phm, struct hpi_response *phr)
{
switch (phm->function) {
case HPI_SUBSYS_OPEN:
case HPI_SUBSYS_CLOSE:
case HPI_SUBSYS_GET_INFO:
case HPI_SUBSYS_DRIVER_UNLOAD:
case HPI_SUBSYS_DRIVER_LOAD:
case HPI_SUBSYS_FIND_ADAPTERS:
/* messages that should not get here */
phr->error = HPI_ERROR_UNIMPLEMENTED;
break;
case HPI_SUBSYS_CREATE_ADAPTER:
subsys_create_adapter(phm, phr);
break;
case HPI_SUBSYS_DELETE_ADAPTER:
subsys_delete_adapter(phm, phr);
break;
default:
phr->error = HPI_ERROR_INVALID_FUNC;
break;
}
}
static void control_message(struct hpi_adapter_obj *pao,
struct hpi_message *phm, struct hpi_response *phr)
{
switch (phm->function) {
case HPI_CONTROL_GET_STATE:
if (pao->has_control_cache) {
u16 err;
err = hpi6000_update_control_cache(pao, phm);
if (err) {
phr->error = err;
break;
}
if (hpi_check_control_cache(((struct hpi_hw_obj *)
pao->priv)->p_cache, phm,
phr))
break;
}
hw_message(pao, phm, phr);
break;
case HPI_CONTROL_GET_INFO:
hw_message(pao, phm, phr);
break;
case HPI_CONTROL_SET_STATE:
hw_message(pao, phm, phr);
hpi_sync_control_cache(((struct hpi_hw_obj *)pao->priv)->
p_cache, phm, phr);
break;
default:
phr->error = HPI_ERROR_INVALID_FUNC;
break;
}
}
static void adapter_message(struct hpi_adapter_obj *pao,
struct hpi_message *phm, struct hpi_response *phr)
{
switch (phm->function) {
case HPI_ADAPTER_GET_INFO:
hw_message(pao, phm, phr);
break;
case HPI_ADAPTER_GET_ASSERT:
adapter_get_asserts(pao, phm, phr);
break;
case HPI_ADAPTER_OPEN:
case HPI_ADAPTER_CLOSE:
case HPI_ADAPTER_TEST_ASSERT:
case HPI_ADAPTER_SELFTEST:
case HPI_ADAPTER_GET_MODE:
case HPI_ADAPTER_SET_MODE:
case HPI_ADAPTER_FIND_OBJECT:
case HPI_ADAPTER_GET_PROPERTY:
case HPI_ADAPTER_SET_PROPERTY:
case HPI_ADAPTER_ENUM_PROPERTY:
hw_message(pao, phm, phr);
break;
default:
phr->error = HPI_ERROR_INVALID_FUNC;
break;
}
}
static void outstream_message(struct hpi_adapter_obj *pao,
struct hpi_message *phm, struct hpi_response *phr)
{
switch (phm->function) {
case HPI_OSTREAM_HOSTBUFFER_ALLOC:
case HPI_OSTREAM_HOSTBUFFER_FREE:
/* Don't let these messages go to the HW function because
* they're called without allocating the spinlock.
* For the HPI6000 adapters the HW would return
* HPI_ERROR_INVALID_FUNC anyway.
*/
phr->error = HPI_ERROR_INVALID_FUNC;
break;
default:
hw_message(pao, phm, phr);
return;
}
}
static void instream_message(struct hpi_adapter_obj *pao,
struct hpi_message *phm, struct hpi_response *phr)
{
switch (phm->function) {
case HPI_ISTREAM_HOSTBUFFER_ALLOC:
case HPI_ISTREAM_HOSTBUFFER_FREE:
/* Don't let these messages go to the HW function because
* they're called without allocating the spinlock.
* For the HPI6000 adapters the HW would return
* HPI_ERROR_INVALID_FUNC anyway.
*/
phr->error = HPI_ERROR_INVALID_FUNC;
break;
default:
hw_message(pao, phm, phr);
return;
}
}
/************************************************************************/
/** HPI_6000()
* Entry point from HPIMAN
* All calls to the HPI start here
*/
void HPI_6000(struct hpi_message *phm, struct hpi_response *phr)
{
struct hpi_adapter_obj *pao = NULL;
/* subsytem messages get executed by every HPI. */
/* All other messages are ignored unless the adapter index matches */
/* an adapter in the HPI */
HPI_DEBUG_LOG(DEBUG, "O %d,F %x\n", phm->object, phm->function);
/* if Dsp has crashed then do not communicate with it any more */
if (phm->object != HPI_OBJ_SUBSYSTEM) {
pao = hpi_find_adapter(phm->adapter_index);
if (!pao) {
HPI_DEBUG_LOG(DEBUG,
" %d,%d refused, for another HPI?\n",
phm->object, phm->function);
return;
}
if (pao->dsp_crashed >= 10) {
hpi_init_response(phr, phm->object, phm->function,
HPI_ERROR_DSP_HARDWARE);
HPI_DEBUG_LOG(DEBUG, " %d,%d dsp crashed.\n",
phm->object, phm->function);
return;
}
}
/* Init default response including the size field */
if (phm->function != HPI_SUBSYS_CREATE_ADAPTER)
hpi_init_response(phr, phm->object, phm->function,
HPI_ERROR_PROCESSING_MESSAGE);
switch (phm->type) {
case HPI_TYPE_MESSAGE:
switch (phm->object) {
case HPI_OBJ_SUBSYSTEM:
subsys_message(phm, phr);
break;
case HPI_OBJ_ADAPTER:
phr->size =
sizeof(struct hpi_response_header) +
sizeof(struct hpi_adapter_res);
adapter_message(pao, phm, phr);
break;
case HPI_OBJ_CONTROL:
control_message(pao, phm, phr);
break;
case HPI_OBJ_OSTREAM:
outstream_message(pao, phm, phr);
break;
case HPI_OBJ_ISTREAM:
instream_message(pao, phm, phr);
break;
default:
hw_message(pao, phm, phr);
break;
}
break;
default:
phr->error = HPI_ERROR_INVALID_TYPE;
break;
}
}
/************************************************************************/
/* SUBSYSTEM */
/* create an adapter object and initialise it based on resource information
* passed in in the message
* NOTE - you cannot use this function AND the FindAdapters function at the
* same time, the application must use only one of them to get the adapters
*/
static void subsys_create_adapter(struct hpi_message *phm,
struct hpi_response *phr)
{
/* create temp adapter obj, because we don't know what index yet */
struct hpi_adapter_obj ao;
struct hpi_adapter_obj *pao;
u32 os_error_code;
short error = 0;
u32 dsp_index = 0;
HPI_DEBUG_LOG(VERBOSE, "subsys_create_adapter\n");
memset(&ao, 0, sizeof(ao));
/* this HPI only creates adapters for TI/PCI2040 based devices */
if (phm->u.s.resource.bus_type != HPI_BUS_PCI)
return;
if (phm->u.s.resource.r.pci->vendor_id != HPI_PCI_VENDOR_ID_TI)
return;
if (phm->u.s.resource.r.pci->device_id != HPI_PCI_DEV_ID_PCI2040)
return;
ao.priv = kmalloc(sizeof(struct hpi_hw_obj), GFP_KERNEL);
if (!ao.priv) {
HPI_DEBUG_LOG(ERROR, "cant get mem for adapter object\n");
phr->error = HPI_ERROR_MEMORY_ALLOC;
return;
}
memset(ao.priv, 0, sizeof(struct hpi_hw_obj));
/* create the adapter object based on the resource information */
/*? memcpy(&ao.Pci,&phm->u.s.Resource.r.Pci,sizeof(ao.Pci)); */
ao.pci = *phm->u.s.resource.r.pci;
error = create_adapter_obj(&ao, &os_error_code);
if (!error)
error = hpi_add_adapter(&ao);
if (error) {
phr->u.s.data = os_error_code;
kfree(ao.priv);
phr->error = error;
return;
}
/* need to update paParentAdapter */
pao = hpi_find_adapter(ao.index);
if (!pao) {
/* We just added this adapter, why can't we find it!? */
HPI_DEBUG_LOG(ERROR, "lost adapter after boot\n");
phr->error = 950;
return;
}
for (dsp_index = 0; dsp_index < MAX_DSPS; dsp_index++) {
struct hpi_hw_obj *phw = (struct hpi_hw_obj *)pao->priv;
phw->ado[dsp_index].pa_parent_adapter = pao;
}
phr->u.s.aw_adapter_list[ao.index] = ao.adapter_type;
phr->u.s.adapter_index = ao.index;
phr->u.s.num_adapters++;
phr->error = 0;
}
static void subsys_delete_adapter(struct hpi_message *phm,
struct hpi_response *phr)
{
struct hpi_adapter_obj *pao = NULL;
struct hpi_hw_obj *phw;
pao = hpi_find_adapter(phm->adapter_index);
if (!pao)
return;
phw = (struct hpi_hw_obj *)pao->priv;
if (pao->has_control_cache)
hpi_free_control_cache(phw->p_cache);
hpi_delete_adapter(pao);
kfree(phw);
phr->error = 0;
}
/* this routine is called from SubSysFindAdapter and SubSysCreateAdapter */
static short create_adapter_obj(struct hpi_adapter_obj *pao,
u32 *pos_error_code)
{
short boot_error = 0;
u32 dsp_index = 0;
u32 control_cache_size = 0;
u32 control_cache_count = 0;
struct hpi_hw_obj *phw = (struct hpi_hw_obj *)pao->priv;
/* init error reporting */
pao->dsp_crashed = 0;
/* The PCI2040 has the following address map */
/* BAR0 - 4K = HPI control and status registers on PCI2040 (HPI CSR) */
/* BAR1 - 32K = HPI registers on DSP */
phw->dw2040_HPICSR = pao->pci.ap_mem_base[0];
phw->dw2040_HPIDSP = pao->pci.ap_mem_base[1];
HPI_DEBUG_LOG(VERBOSE, "csr %p, dsp %p\n", phw->dw2040_HPICSR,
phw->dw2040_HPIDSP);
/* set addresses for the possible DSP HPI interfaces */
for (dsp_index = 0; dsp_index < MAX_DSPS; dsp_index++) {
phw->ado[dsp_index].prHPI_control =
phw->dw2040_HPIDSP + (CONTROL +
DSP_SPACING * dsp_index);
phw->ado[dsp_index].prHPI_address =
phw->dw2040_HPIDSP + (ADDRESS +
DSP_SPACING * dsp_index);
phw->ado[dsp_index].prHPI_data =
phw->dw2040_HPIDSP + (DATA + DSP_SPACING * dsp_index);
phw->ado[dsp_index].prHPI_data_auto_inc =
phw->dw2040_HPIDSP + (DATA_AUTOINC +
DSP_SPACING * dsp_index);
HPI_DEBUG_LOG(VERBOSE, "ctl %p, adr %p, dat %p, dat++ %p\n",
phw->ado[dsp_index].prHPI_control,
phw->ado[dsp_index].prHPI_address,
phw->ado[dsp_index].prHPI_data,
phw->ado[dsp_index].prHPI_data_auto_inc);
phw->ado[dsp_index].pa_parent_adapter = pao;
}
phw->pCI2040HPI_error_count = 0;
pao->has_control_cache = 0;
/* Set the default number of DSPs on this card */
/* This is (conditionally) adjusted after bootloading */
/* of the first DSP in the bootload section. */
phw->num_dsp = 1;
boot_error = hpi6000_adapter_boot_load_dsp(pao, pos_error_code);
if (boot_error)
return boot_error;
HPI_DEBUG_LOG(INFO, "bootload DSP OK\n");
phw->message_buffer_address_on_dsp = 0L;
phw->response_buffer_address_on_dsp = 0L;
/* get info about the adapter by asking the adapter */
/* send a HPI_ADAPTER_GET_INFO message */
{
struct hpi_message hM;
struct hpi_response hR0; /* response from DSP 0 */
struct hpi_response hR1; /* response from DSP 1 */
u16 error = 0;
HPI_DEBUG_LOG(VERBOSE, "send ADAPTER_GET_INFO\n");
memset(&hM, 0, sizeof(hM));
hM.type = HPI_TYPE_MESSAGE;
hM.size = sizeof(struct hpi_message);
hM.object = HPI_OBJ_ADAPTER;
hM.function = HPI_ADAPTER_GET_INFO;
hM.adapter_index = 0;
memset(&hR0, 0, sizeof(hR0));
memset(&hR1, 0, sizeof(hR1));
hR0.size = sizeof(hR0);
hR1.size = sizeof(hR1);
error = hpi6000_message_response_sequence(pao, 0, &hM, &hR0);
if (hR0.error) {
HPI_DEBUG_LOG(DEBUG, "message error %d\n", hR0.error);
return hR0.error;
}
if (phw->num_dsp == 2) {
error = hpi6000_message_response_sequence(pao, 1, &hM,
&hR1);
if (error)
return error;
}
pao->adapter_type = hR0.u.a.adapter_type;
pao->index = hR0.u.a.adapter_index;
}
memset(&phw->control_cache[0], 0,
sizeof(struct hpi_control_cache_single) *
HPI_NMIXER_CONTROLS);
/* Read the control cache length to figure out if it is turned on */
control_cache_size =
hpi_read_word(&phw->ado[0],
HPI_HIF_ADDR(control_cache_size_in_bytes));
if (control_cache_size) {
control_cache_count =
hpi_read_word(&phw->ado[0],
HPI_HIF_ADDR(control_cache_count));
pao->has_control_cache = 1;
phw->p_cache =
hpi_alloc_control_cache(control_cache_count,
control_cache_size, (struct hpi_control_cache_info *)
&phw->control_cache[0]
);
} else
pao->has_control_cache = 0;
HPI_DEBUG_LOG(DEBUG, "get adapter info ASI%04X index %d\n",
pao->adapter_type, pao->index);
pao->open = 0; /* upon creation the adapter is closed */
return 0;
}
/************************************************************************/
/* ADAPTER */
static void adapter_get_asserts(struct hpi_adapter_obj *pao,
struct hpi_message *phm, struct hpi_response *phr)
{
#ifndef HIDE_PCI_ASSERTS
/* if we have PCI2040 asserts then collect them */
if ((gw_pci_read_asserts > 0) || (gw_pci_write_asserts > 0)) {
phr->u.a.serial_number =
gw_pci_read_asserts * 100 + gw_pci_write_asserts;
phr->u.a.adapter_index = 1; /* assert count */
phr->u.a.adapter_type = -1; /* "dsp index" */
strcpy(phr->u.a.sz_adapter_assert, "PCI2040 error");
gw_pci_read_asserts = 0;
gw_pci_write_asserts = 0;
phr->error = 0;
} else
#endif
hw_message(pao, phm, phr); /*get DSP asserts */
return;
}
/************************************************************************/
/* LOW-LEVEL */
static short hpi6000_adapter_boot_load_dsp(struct hpi_adapter_obj *pao,
u32 *pos_error_code)
{
struct hpi_hw_obj *phw = (struct hpi_hw_obj *)pao->priv;
short error;
u32 timeout;
u32 read = 0;
u32 i = 0;
u32 data = 0;
u32 j = 0;
u32 test_addr = 0x80000000;
u32 test_data = 0x00000001;
u32 dw2040_reset = 0;
u32 dsp_index = 0;
u32 endian = 0;
u32 adapter_info = 0;
u32 delay = 0;
struct dsp_code dsp_code;
u16 boot_load_family = 0;
/* NOTE don't use wAdapterType in this routine. It is not setup yet */
switch (pao->pci.subsys_device_id) {
case 0x5100:
case 0x5110: /* ASI5100 revB or higher with C6711D */
case 0x6100:
case 0x6200:
boot_load_family = HPI_ADAPTER_FAMILY_ASI(0x6200);
break;
case 0x8800:
boot_load_family = HPI_ADAPTER_FAMILY_ASI(0x8800);
break;
default:
return HPI6000_ERROR_UNHANDLED_SUBSYS_ID;
}
/* reset all DSPs, indicate two DSPs are present
* set RST3-=1 to disconnect HAD8 to set DSP in little endian mode
*/
endian = 0;
dw2040_reset = 0x0003000F;
iowrite32(dw2040_reset, phw->dw2040_HPICSR + HPI_RESET);
/* read back register to make sure PCI2040 chip is functioning
* note that bits 4..15 are read-only and so should always return zero,
* even though we wrote 1 to them
*/
for (i = 0; i < 1000; i++)
delay = ioread32(phw->dw2040_HPICSR + HPI_RESET);
if (delay != dw2040_reset) {
HPI_DEBUG_LOG(ERROR, "INIT_PCI2040 %x %x\n", dw2040_reset,
delay);
return HPI6000_ERROR_INIT_PCI2040;
}
/* Indicate that DSP#0,1 is a C6X */
iowrite32(0x00000003, phw->dw2040_HPICSR + HPI_DATA_WIDTH);
/* set Bit30 and 29 - which will prevent Target aborts from being
* issued upon HPI or GP error
*/
iowrite32(0x60000000, phw->dw2040_HPICSR + INTERRUPT_MASK_SET);
/* isolate DSP HAD8 line from PCI2040 so that
* Little endian can be set by pullup
*/
dw2040_reset = dw2040_reset & (~(endian << 3));
iowrite32(dw2040_reset, phw->dw2040_HPICSR + HPI_RESET);
phw->ado[0].c_dsp_rev = 'B'; /* revB */
phw->ado[1].c_dsp_rev = 'B'; /* revB */
/*Take both DSPs out of reset, setting HAD8 to the correct Endian */
dw2040_reset = dw2040_reset & (~0x00000001); /* start DSP 0 */
iowrite32(dw2040_reset, phw->dw2040_HPICSR + HPI_RESET);
dw2040_reset = dw2040_reset & (~0x00000002); /* start DSP 1 */
iowrite32(dw2040_reset, phw->dw2040_HPICSR + HPI_RESET);
/* set HAD8 back to PCI2040, now that DSP set to little endian mode */
dw2040_reset = dw2040_reset & (~0x00000008);
iowrite32(dw2040_reset, phw->dw2040_HPICSR + HPI_RESET);
/*delay to allow DSP to get going */
for (i = 0; i < 100; i++)
delay = ioread32(phw->dw2040_HPICSR + HPI_RESET);
/* loop through all DSPs, downloading DSP code */
for (dsp_index = 0; dsp_index < phw->num_dsp; dsp_index++) {
struct dsp_obj *pdo = &phw->ado[dsp_index];
/* configure DSP so that we download code into the SRAM */
/* set control reg for little endian, HWOB=1 */
iowrite32(0x00010001, pdo->prHPI_control);
/* test access to the HPI address register (HPIA) */
test_data = 0x00000001;
for (j = 0; j < 32; j++) {
iowrite32(test_data, pdo->prHPI_address);
data = ioread32(pdo->prHPI_address);
if (data != test_data) {
HPI_DEBUG_LOG(ERROR, "INIT_DSPHPI %x %x %x\n",
test_data, data, dsp_index);
return HPI6000_ERROR_INIT_DSPHPI;
}
test_data = test_data << 1;
}
/* if C6713 the setup PLL to generate 225MHz from 25MHz.
* Since the PLLDIV1 read is sometimes wrong, even on a C6713,
* we're going to do this unconditionally
*/
/* PLLDIV1 should have a value of 8000 after reset */
/*
if (HpiReadWord(pdo,0x01B7C118) == 0x8000)
*/
{
/* C6713 datasheet says we cannot program PLL from HPI,
* and indeed if we try to set the PLL multiply from the
* HPI, the PLL does not seem to lock,
* so we enable the PLL and use the default of x 7
*/
/* bypass PLL */
hpi_write_word(pdo, 0x01B7C100, 0x0000);
for (i = 0; i < 100; i++)
delay = ioread32(phw->dw2040_HPICSR +
HPI_RESET);
/* ** use default of PLL x7 ** */
/* EMIF = 225/3=75MHz */
hpi_write_word(pdo, 0x01B7C120, 0x8002);
/* peri = 225/2 */
hpi_write_word(pdo, 0x01B7C11C, 0x8001);
/* cpu = 225/1 */
hpi_write_word(pdo, 0x01B7C118, 0x8000);
/* ~200us delay */
for (i = 0; i < 2000; i++)
delay = ioread32(phw->dw2040_HPICSR +
HPI_RESET);
/* PLL not bypassed */
hpi_write_word(pdo, 0x01B7C100, 0x0001);
/* ~200us delay */
for (i = 0; i < 2000; i++)
delay = ioread32(phw->dw2040_HPICSR +
HPI_RESET);
}
/* test r/w to internal DSP memory
* C6711 has L2 cache mapped to 0x0 when reset
*
* revB - because of bug 3.0.1 last HPI read
* (before HPI address issued) must be non-autoinc
*/
/* test each bit in the 32bit word */
for (i = 0; i < 100; i++) {
test_addr = 0x00000000;
test_data = 0x00000001;
for (j = 0; j < 32; j++) {
hpi_write_word(pdo, test_addr + i, test_data);
data = hpi_read_word(pdo, test_addr + i);
if (data != test_data) {
HPI_DEBUG_LOG(ERROR,
"DSP mem %x %x %x %x\n",
test_addr + i, test_data,
data, dsp_index);
return HPI6000_ERROR_INIT_DSPINTMEM;
}
test_data = test_data << 1;
}
}
/* memory map of ASI6200
00000000-0000FFFF 16Kx32 internal program
01800000-019FFFFF Internal peripheral
80000000-807FFFFF CE0 2Mx32 SDRAM running @ 100MHz
90000000-9000FFFF CE1 Async peripherals:
EMIF config
------------
Global EMIF control
0 -
1 -
2 -
3 CLK2EN = 1 CLKOUT2 enabled
4 CLK1EN = 0 CLKOUT1 disabled
5 EKEN = 1 <--!! C6713 specific, enables ECLKOUT
6 -
7 NOHOLD = 1 external HOLD disabled
8 HOLDA = 0 HOLDA output is low
9 HOLD = 0 HOLD input is low
10 ARDY = 1 ARDY input is high
11 BUSREQ = 0 BUSREQ output is low
12,13 Reserved = 1
*/
hpi_write_word(pdo, 0x01800000, 0x34A8);
/* EMIF CE0 setup - 2Mx32 Sync DRAM
31..28 Wr setup
27..22 Wr strobe
21..20 Wr hold
19..16 Rd setup
15..14 -
13..8 Rd strobe
7..4 MTYPE 0011 Sync DRAM 32bits
3 Wr hold MSB
2..0 Rd hold
*/
hpi_write_word(pdo, 0x01800008, 0x00000030);
/* EMIF SDRAM Extension
31-21 0
20 WR2RD = 0
19-18 WR2DEAC = 1
17 WR2WR = 0
16-15 R2WDQM = 2
14-12 RD2WR = 4
11-10 RD2DEAC = 1
9 RD2RD = 1
8-7 THZP = 10b
6-5 TWR = 2-1 = 01b (tWR = 10ns)
4 TRRD = 0b = 2 ECLK (tRRD = 14ns)
3-1 TRAS = 5-1 = 100b (Tras=42ns = 5 ECLK)
1 CAS latency = 3 ECLK
(for Micron 2M32-7 operating at 100Mhz)
*/
/* need to use this else DSP code crashes */
hpi_write_word(pdo, 0x01800020, 0x001BDF29);
/* EMIF SDRAM control - set up for a 2Mx32 SDRAM (512x32x4 bank)
31 - -
30 SDBSZ 1 4 bank
29..28 SDRSZ 00 11 row address pins
27..26 SDCSZ 01 8 column address pins
25 RFEN 1 refersh enabled
24 INIT 1 init SDRAM
23..20 TRCD 0001
19..16 TRP 0001
15..12 TRC 0110
11..0 - -
*/
/* need to use this else DSP code crashes */
hpi_write_word(pdo, 0x01800018, 0x47117000);
/* EMIF SDRAM Refresh Timing */
hpi_write_word(pdo, 0x0180001C, 0x00000410);
/*MIF CE1 setup - Async peripherals
@100MHz bus speed, each cycle is 10ns,
31..28 Wr setup = 1
27..22 Wr strobe = 3 30ns
21..20 Wr hold = 1
19..16 Rd setup =1
15..14 Ta = 2
13..8 Rd strobe = 3 30ns
7..4 MTYPE 0010 Async 32bits
3 Wr hold MSB =0
2..0 Rd hold = 1
*/
{
u32 cE1 =
(1L << 28) | (3L << 22) | (1L << 20) | (1L <<
16) | (2L << 14) | (3L << 8) | (2L << 4) | 1L;
hpi_write_word(pdo, 0x01800004, cE1);
}
/* delay a little to allow SDRAM and DSP to "get going" */
for (i = 0; i < 1000; i++)
delay = ioread32(phw->dw2040_HPICSR + HPI_RESET);
/* test access to SDRAM */
{
test_addr = 0x80000000;
test_data = 0x00000001;
/* test each bit in the 32bit word */
for (j = 0; j < 32; j++) {
hpi_write_word(pdo, test_addr, test_data);
data = hpi_read_word(pdo, test_addr);
if (data != test_data) {
HPI_DEBUG_LOG(ERROR,
"DSP dram %x %x %x %x\n",
test_addr, test_data, data,
dsp_index);
return HPI6000_ERROR_INIT_SDRAM1;
}
test_data = test_data << 1;
}
/* test every Nth address in the DRAM */
#define DRAM_SIZE_WORDS 0x200000 /*2_mx32 */
#define DRAM_INC 1024
test_addr = 0x80000000;
test_data = 0x0;
for (i = 0; i < DRAM_SIZE_WORDS; i = i + DRAM_INC) {
hpi_write_word(pdo, test_addr + i, test_data);
test_data++;
}
test_addr = 0x80000000;
test_data = 0x0;
for (i = 0; i < DRAM_SIZE_WORDS; i = i + DRAM_INC) {
data = hpi_read_word(pdo, test_addr + i);
if (data != test_data) {
HPI_DEBUG_LOG(ERROR,
"DSP dram %x %x %x %x\n",
test_addr + i, test_data,
data, dsp_index);
return HPI6000_ERROR_INIT_SDRAM2;
}
test_data++;
}
}
/* write the DSP code down into the DSPs memory */
/*HpiDspCode_Open(nBootLoadFamily,&DspCode,pdwOsErrorCode); */
dsp_code.ps_dev = pao->pci.p_os_data;
error = hpi_dsp_code_open(boot_load_family, &dsp_code,
pos_error_code);
if (error)
return error;
while (1) {
u32 length;
u32 address;
u32 type;
u32 *pcode;
error = hpi_dsp_code_read_word(&dsp_code, &length);
if (error)
break;
if (length == 0xFFFFFFFF)
break; /* end of code */
error = hpi_dsp_code_read_word(&dsp_code, &address);
if (error)
break;
error = hpi_dsp_code_read_word(&dsp_code, &type);
if (error)
break;
error = hpi_dsp_code_read_block(length, &dsp_code,
&pcode);
if (error)
break;
error = hpi6000_dsp_block_write32(pao, (u16)dsp_index,
address, pcode, length);
if (error)
break;
}
if (error) {
hpi_dsp_code_close(&dsp_code);
return error;
}
/* verify that code was written correctly */
/* this time through, assume no errors in DSP code file/array */
hpi_dsp_code_rewind(&dsp_code);
while (1) {
u32 length;
u32 address;
u32 type;
u32 *pcode;
hpi_dsp_code_read_word(&dsp_code, &length);
if (length == 0xFFFFFFFF)
break; /* end of code */
hpi_dsp_code_read_word(&dsp_code, &address);
hpi_dsp_code_read_word(&dsp_code, &type);
hpi_dsp_code_read_block(length, &dsp_code, &pcode);
for (i = 0; i < length; i++) {
data = hpi_read_word(pdo, address);
if (data != *pcode) {
error = HPI6000_ERROR_INIT_VERIFY;
HPI_DEBUG_LOG(ERROR,
"DSP verify %x %x %x %x\n",
address, *pcode, data,
dsp_index);
break;
}
pcode++;
address += 4;
}
if (error)
break;
}
hpi_dsp_code_close(&dsp_code);
if (error)
return error;
/* zero out the hostmailbox */
{
u32 address = HPI_HIF_ADDR(host_cmd);
for (i = 0; i < 4; i++) {
hpi_write_word(pdo, address, 0);
address += 4;
}
}
/* write the DSP number into the hostmailbox */
/* structure before starting the DSP */
hpi_write_word(pdo, HPI_HIF_ADDR(dsp_number), dsp_index);
/* write the DSP adapter Info into the */
/* hostmailbox before starting the DSP */
if (dsp_index > 0)
hpi_write_word(pdo, HPI_HIF_ADDR(adapter_info),
adapter_info);
/* step 3. Start code by sending interrupt */
iowrite32(0x00030003, pdo->prHPI_control);
for (i = 0; i < 10000; i++)
delay = ioread32(phw->dw2040_HPICSR + HPI_RESET);
/* wait for a non-zero value in hostcmd -
* indicating initialization is complete
*
* Init could take a while if DSP checks SDRAM memory
* Was 200000. Increased to 2000000 for ASI8801 so we
* don't get 938 errors.
*/
timeout = 2000000;
while (timeout) {
do {
read = hpi_read_word(pdo,
HPI_HIF_ADDR(host_cmd));
} while (--timeout
&& hpi6000_check_PCI2040_error_flag(pao,
H6READ));
if (read)
break;
/* The following is a workaround for bug #94:
* Bluescreen on install and subsequent boots on a
* DELL PowerEdge 600SC PC with 1.8GHz P4 and
* ServerWorks chipset. Without this delay the system
* locks up with a bluescreen (NOT GPF or pagefault).
*/
else
hpios_delay_micro_seconds(1000);
}
if (timeout == 0)
return HPI6000_ERROR_INIT_NOACK;
/* read the DSP adapter Info from the */
/* hostmailbox structure after starting the DSP */
if (dsp_index == 0) {
/*u32 dwTestData=0; */
u32 mask = 0;
adapter_info =
hpi_read_word(pdo,
HPI_HIF_ADDR(adapter_info));
if (HPI_ADAPTER_FAMILY_ASI
(HPI_HIF_ADAPTER_INFO_EXTRACT_ADAPTER
(adapter_info)) ==
HPI_ADAPTER_FAMILY_ASI(0x6200))
/* all 6200 cards have this many DSPs */
phw->num_dsp = 2;
/* test that the PLD is programmed */
/* and we can read/write 24bits */
#define PLD_BASE_ADDRESS 0x90000000L /*for ASI6100/6200/8800 */
switch (boot_load_family) {
case HPI_ADAPTER_FAMILY_ASI(0x6200):
/* ASI6100/6200 has 24bit path to FPGA */
mask = 0xFFFFFF00L;
/* ASI5100 uses AX6 code, */
/* but has no PLD r/w register to test */
if (HPI_ADAPTER_FAMILY_ASI(pao->pci.
subsys_device_id) ==
HPI_ADAPTER_FAMILY_ASI(0x5100))
mask = 0x00000000L;
break;
case HPI_ADAPTER_FAMILY_ASI(0x8800):
/* ASI8800 has 16bit path to FPGA */
mask = 0xFFFF0000L;
break;
}
test_data = 0xAAAAAA00L & mask;
/* write to 24 bit Debug register (D31-D8) */
hpi_write_word(pdo, PLD_BASE_ADDRESS + 4L, test_data);
read = hpi_read_word(pdo,
PLD_BASE_ADDRESS + 4L) & mask;
if (read != test_data) {
HPI_DEBUG_LOG(ERROR, "PLD %x %x\n", test_data,
read);
return HPI6000_ERROR_INIT_PLDTEST1;
}
test_data = 0x55555500L & mask;
hpi_write_word(pdo, PLD_BASE_ADDRESS + 4L, test_data);
read = hpi_read_word(pdo,
PLD_BASE_ADDRESS + 4L) & mask;
if (read != test_data) {
HPI_DEBUG_LOG(ERROR, "PLD %x %x\n", test_data,
read);
return HPI6000_ERROR_INIT_PLDTEST2;
}
}
} /* for numDSP */
return 0;
}
#define PCI_TIMEOUT 100
static int hpi_set_address(struct dsp_obj *pdo, u32 address)
{
u32 timeout = PCI_TIMEOUT;
do {
iowrite32(address, pdo->prHPI_address);
} while (hpi6000_check_PCI2040_error_flag(pdo->pa_parent_adapter,
H6WRITE)
&& --timeout);
if (timeout)
return 0;
return 1;
}
/* write one word to the HPI port */
static void hpi_write_word(struct dsp_obj *pdo, u32 address, u32 data)
{
if (hpi_set_address(pdo, address))
return;
iowrite32(data, pdo->prHPI_data);
}
/* read one word from the HPI port */
static u32 hpi_read_word(struct dsp_obj *pdo, u32 address)
{
u32 data = 0;
if (hpi_set_address(pdo, address))
return 0; /*? no way to return error */
/* take care of errata in revB DSP (2.0.1) */
data = ioread32(pdo->prHPI_data);
return data;
}
/* write a block of 32bit words to the DSP HPI port using auto-inc mode */
static void hpi_write_block(struct dsp_obj *pdo, u32 address, u32 *pdata,
u32 length)
{
u16 length16 = length - 1;
if (length == 0)
return;
if (hpi_set_address(pdo, address))
return;
iowrite32_rep(pdo->prHPI_data_auto_inc, pdata, length16);
/* take care of errata in revB DSP (2.0.1) */
/* must end with non auto-inc */
iowrite32(*(pdata + length - 1), pdo->prHPI_data);
}
/** read a block of 32bit words from the DSP HPI port using auto-inc mode
*/
static void hpi_read_block(struct dsp_obj *pdo, u32 address, u32 *pdata,
u32 length)
{
u16 length16 = length - 1;
if (length == 0)
return;
if (hpi_set_address(pdo, address))
return;
ioread32_rep(pdo->prHPI_data_auto_inc, pdata, length16);
/* take care of errata in revB DSP (2.0.1) */
/* must end with non auto-inc */
*(pdata + length - 1) = ioread32(pdo->prHPI_data);
}
static u16 hpi6000_dsp_block_write32(struct hpi_adapter_obj *pao,
u16 dsp_index, u32 hpi_address, u32 *source, u32 count)
{
struct dsp_obj *pdo =
&(*(struct hpi_hw_obj *)pao->priv).ado[dsp_index];
u32 time_out = PCI_TIMEOUT;
int c6711_burst_size = 128;
u32 local_hpi_address = hpi_address;
int local_count = count;
int xfer_size;
u32 *pdata = source;
while (local_count) {
if (local_count > c6711_burst_size)
xfer_size = c6711_burst_size;
else
xfer_size = local_count;
time_out = PCI_TIMEOUT;
do {
hpi_write_block(pdo, local_hpi_address, pdata,
xfer_size);
} while (hpi6000_check_PCI2040_error_flag(pao, H6WRITE)
&& --time_out);
if (!time_out)
break;
pdata += xfer_size;
local_hpi_address += sizeof(u32) * xfer_size;
local_count -= xfer_size;
}
if (time_out)
return 0;
else
return 1;
}
static u16 hpi6000_dsp_block_read32(struct hpi_adapter_obj *pao,
u16 dsp_index, u32 hpi_address, u32 *dest, u32 count)
{
struct dsp_obj *pdo =
&(*(struct hpi_hw_obj *)pao->priv).ado[dsp_index];
u32 time_out = PCI_TIMEOUT;
int c6711_burst_size = 16;
u32 local_hpi_address = hpi_address;
int local_count = count;
int xfer_size;
u32 *pdata = dest;
u32 loop_count = 0;
while (local_count) {
if (local_count > c6711_burst_size)
xfer_size = c6711_burst_size;
else
xfer_size = local_count;
time_out = PCI_TIMEOUT;
do {
hpi_read_block(pdo, local_hpi_address, pdata,
xfer_size);
} while (hpi6000_check_PCI2040_error_flag(pao, H6READ)
&& --time_out);
if (!time_out)
break;
pdata += xfer_size;
local_hpi_address += sizeof(u32) * xfer_size;
local_count -= xfer_size;
loop_count++;
}
if (time_out)
return 0;
else
return 1;
}
static short hpi6000_message_response_sequence(struct hpi_adapter_obj *pao,
u16 dsp_index, struct hpi_message *phm, struct hpi_response *phr)
{
struct hpi_hw_obj *phw = (struct hpi_hw_obj *)pao->priv;
struct dsp_obj *pdo = &phw->ado[dsp_index];
u32 timeout;
u16 ack;
u32 address;
u32 length;
u32 *p_data;
u16 error = 0;
/* does the DSP we are referencing exist? */
if (dsp_index >= phw->num_dsp)
return HPI6000_ERROR_MSG_INVALID_DSP_INDEX;
ack = hpi6000_wait_dsp_ack(pao, dsp_index, HPI_HIF_IDLE);
if (ack & HPI_HIF_ERROR_MASK) {
pao->dsp_crashed++;
return HPI6000_ERROR_MSG_RESP_IDLE_TIMEOUT;
}
pao->dsp_crashed = 0;
/* send the message */
/* get the address and size */
if (phw->message_buffer_address_on_dsp == 0) {
timeout = TIMEOUT;
do {
address =
hpi_read_word(pdo,
HPI_HIF_ADDR(message_buffer_address));
phw->message_buffer_address_on_dsp = address;
} while (hpi6000_check_PCI2040_error_flag(pao, H6READ)
&& --timeout);
if (!timeout)
return HPI6000_ERROR_MSG_GET_ADR;
} else
address = phw->message_buffer_address_on_dsp;
/* dwLength = sizeof(struct hpi_message); */
length = phm->size;
/* send it */
p_data = (u32 *)phm;
if (hpi6000_dsp_block_write32(pao, dsp_index, address, p_data,
(u16)length / 4))
return HPI6000_ERROR_MSG_RESP_BLOCKWRITE32;
if (hpi6000_send_host_command(pao, dsp_index, HPI_HIF_GET_RESP))
return HPI6000_ERROR_MSG_RESP_GETRESPCMD;
hpi6000_send_dsp_interrupt(pdo);
ack = hpi6000_wait_dsp_ack(pao, dsp_index, HPI_HIF_GET_RESP);
if (ack & HPI_HIF_ERROR_MASK)
return HPI6000_ERROR_MSG_RESP_GET_RESP_ACK;
/* get the address and size */
if (phw->response_buffer_address_on_dsp == 0) {
timeout = TIMEOUT;
do {
address =
hpi_read_word(pdo,
HPI_HIF_ADDR(response_buffer_address));
} while (hpi6000_check_PCI2040_error_flag(pao, H6READ)
&& --timeout);
phw->response_buffer_address_on_dsp = address;
if (!timeout)
return HPI6000_ERROR_RESP_GET_ADR;
} else
address = phw->response_buffer_address_on_dsp;
/* read the length of the response back from the DSP */
timeout = TIMEOUT;
do {
length = hpi_read_word(pdo, HPI_HIF_ADDR(length));
} while (hpi6000_check_PCI2040_error_flag(pao, H6READ) && --timeout);
if (!timeout)
length = sizeof(struct hpi_response);
/* get it */
p_data = (u32 *)phr;
if (hpi6000_dsp_block_read32(pao, dsp_index, address, p_data,
(u16)length / 4))
return HPI6000_ERROR_MSG_RESP_BLOCKREAD32;
/* set i/f back to idle */
if (hpi6000_send_host_command(pao, dsp_index, HPI_HIF_IDLE))
return HPI6000_ERROR_MSG_RESP_IDLECMD;
hpi6000_send_dsp_interrupt(pdo);
error = hpi_validate_response(phm, phr);
return error;
}
/* have to set up the below defines to match stuff in the MAP file */
#define MSG_ADDRESS (HPI_HIF_BASE+0x18)
#define MSG_LENGTH 11
#define RESP_ADDRESS (HPI_HIF_BASE+0x44)
#define RESP_LENGTH 16
#define QUEUE_START (HPI_HIF_BASE+0x88)
#define QUEUE_SIZE 0x8000
static short hpi6000_send_data_check_adr(u32 address, u32 length_in_dwords)
{
/*#define CHECKING // comment this line in to enable checking */
#ifdef CHECKING
if (address < (u32)MSG_ADDRESS)
return 0;
if (address > (u32)(QUEUE_START + QUEUE_SIZE))
return 0;
if ((address + (length_in_dwords << 2)) >
(u32)(QUEUE_START + QUEUE_SIZE))
return 0;
#else
(void)address;
(void)length_in_dwords;
return 1;
#endif
}
static short hpi6000_send_data(struct hpi_adapter_obj *pao, u16 dsp_index,
struct hpi_message *phm, struct hpi_response *phr)
{
struct dsp_obj *pdo =
&(*(struct hpi_hw_obj *)pao->priv).ado[dsp_index];
u32 data_sent = 0;
u16 ack;
u32 length, address;
u32 *p_data = (u32 *)phm->u.d.u.data.pb_data;
u16 time_out = 8;
(void)phr;
/* round dwDataSize down to nearest 4 bytes */
while ((data_sent < (phm->u.d.u.data.data_size & ~3L))
&& --time_out) {
ack = hpi6000_wait_dsp_ack(pao, dsp_index, HPI_HIF_IDLE);
if (ack & HPI_HIF_ERROR_MASK)
return HPI6000_ERROR_SEND_DATA_IDLE_TIMEOUT;
if (hpi6000_send_host_command(pao, dsp_index,
HPI_HIF_SEND_DATA))
return HPI6000_ERROR_SEND_DATA_CMD;
hpi6000_send_dsp_interrupt(pdo);
ack = hpi6000_wait_dsp_ack(pao, dsp_index, HPI_HIF_SEND_DATA);
if (ack & HPI_HIF_ERROR_MASK)
return HPI6000_ERROR_SEND_DATA_ACK;
do {
/* get the address and size */
address = hpi_read_word(pdo, HPI_HIF_ADDR(address));
/* DSP returns number of DWORDS */
length = hpi_read_word(pdo, HPI_HIF_ADDR(length));
} while (hpi6000_check_PCI2040_error_flag(pao, H6READ));
if (!hpi6000_send_data_check_adr(address, length))
return HPI6000_ERROR_SEND_DATA_ADR;
/* send the data. break data into 512 DWORD blocks (2K bytes)
* and send using block write. 2Kbytes is the max as this is the
* memory window given to the HPI data register by the PCI2040
*/
{
u32 len = length;
u32 blk_len = 512;
while (len) {
if (len < blk_len)
blk_len = len;
if (hpi6000_dsp_block_write32(pao, dsp_index,
address, p_data, blk_len))
return HPI6000_ERROR_SEND_DATA_WRITE;
address += blk_len * 4;
p_data += blk_len;
len -= blk_len;
}
}
if (hpi6000_send_host_command(pao, dsp_index, HPI_HIF_IDLE))
return HPI6000_ERROR_SEND_DATA_IDLECMD;
hpi6000_send_dsp_interrupt(pdo);
data_sent += length * 4;
}
if (!time_out)
return HPI6000_ERROR_SEND_DATA_TIMEOUT;
return 0;
}
static short hpi6000_get_data(struct hpi_adapter_obj *pao, u16 dsp_index,
struct hpi_message *phm, struct hpi_response *phr)
{
struct dsp_obj *pdo =
&(*(struct hpi_hw_obj *)pao->priv).ado[dsp_index];
u32 data_got = 0;
u16 ack;
u32 length, address;
u32 *p_data = (u32 *)phm->u.d.u.data.pb_data;
(void)phr; /* this parameter not used! */
/* round dwDataSize down to nearest 4 bytes */
while (data_got < (phm->u.d.u.data.data_size & ~3L)) {
ack = hpi6000_wait_dsp_ack(pao, dsp_index, HPI_HIF_IDLE);
if (ack & HPI_HIF_ERROR_MASK)
return HPI6000_ERROR_GET_DATA_IDLE_TIMEOUT;
if (hpi6000_send_host_command(pao, dsp_index,
HPI_HIF_GET_DATA))
return HPI6000_ERROR_GET_DATA_CMD;
hpi6000_send_dsp_interrupt(pdo);
ack = hpi6000_wait_dsp_ack(pao, dsp_index, HPI_HIF_GET_DATA);
if (ack & HPI_HIF_ERROR_MASK)
return HPI6000_ERROR_GET_DATA_ACK;
/* get the address and size */
do {
address = hpi_read_word(pdo, HPI_HIF_ADDR(address));
length = hpi_read_word(pdo, HPI_HIF_ADDR(length));
} while (hpi6000_check_PCI2040_error_flag(pao, H6READ));
/* read the data */
{
u32 len = length;
u32 blk_len = 512;
while (len) {
if (len < blk_len)
blk_len = len;
if (hpi6000_dsp_block_read32(pao, dsp_index,
address, p_data, blk_len))
return HPI6000_ERROR_GET_DATA_READ;
address += blk_len * 4;
p_data += blk_len;
len -= blk_len;
}
}
if (hpi6000_send_host_command(pao, dsp_index, HPI_HIF_IDLE))
return HPI6000_ERROR_GET_DATA_IDLECMD;
hpi6000_send_dsp_interrupt(pdo);
data_got += length * 4;
}
return 0;
}
static void hpi6000_send_dsp_interrupt(struct dsp_obj *pdo)
{
iowrite32(0x00030003, pdo->prHPI_control); /* DSPINT */
}
static short hpi6000_send_host_command(struct hpi_adapter_obj *pao,
u16 dsp_index, u32 host_cmd)
{
struct dsp_obj *pdo =
&(*(struct hpi_hw_obj *)pao->priv).ado[dsp_index];
u32 timeout = TIMEOUT;
/* set command */
do {
hpi_write_word(pdo, HPI_HIF_ADDR(host_cmd), host_cmd);
/* flush the FIFO */
hpi_set_address(pdo, HPI_HIF_ADDR(host_cmd));
} while (hpi6000_check_PCI2040_error_flag(pao, H6WRITE) && --timeout);
/* reset the interrupt bit */
iowrite32(0x00040004, pdo->prHPI_control);
if (timeout)
return 0;
else
return 1;
}
/* if the PCI2040 has recorded an HPI timeout, reset the error and return 1 */
static short hpi6000_check_PCI2040_error_flag(struct hpi_adapter_obj *pao,
u16 read_or_write)
{
u32 hPI_error;
struct hpi_hw_obj *phw = (struct hpi_hw_obj *)pao->priv;
/* read the error bits from the PCI2040 */
hPI_error = ioread32(phw->dw2040_HPICSR + HPI_ERROR_REPORT);
if (hPI_error) {
/* reset the error flag */
iowrite32(0L, phw->dw2040_HPICSR + HPI_ERROR_REPORT);
phw->pCI2040HPI_error_count++;
if (read_or_write == 1)
gw_pci_read_asserts++; /************* inc global */
else
gw_pci_write_asserts++;
return 1;
} else
return 0;
}
static short hpi6000_wait_dsp_ack(struct hpi_adapter_obj *pao, u16 dsp_index,
u32 ack_value)
{
struct dsp_obj *pdo =
&(*(struct hpi_hw_obj *)pao->priv).ado[dsp_index];
u32 ack = 0L;
u32 timeout;
u32 hPIC = 0L;
/* wait for host interrupt to signal ack is ready */
timeout = TIMEOUT;
while (--timeout) {
hPIC = ioread32(pdo->prHPI_control);
if (hPIC & 0x04) /* 0x04 = HINT from DSP */
break;
}
if (timeout == 0)
return HPI_HIF_ERROR_MASK;
/* wait for dwAckValue */
timeout = TIMEOUT;
while (--timeout) {
/* read the ack mailbox */
ack = hpi_read_word(pdo, HPI_HIF_ADDR(dsp_ack));
if (ack == ack_value)
break;
if ((ack & HPI_HIF_ERROR_MASK)
&& !hpi6000_check_PCI2040_error_flag(pao, H6READ))
break;
/*for (i=0;i<1000;i++) */
/* dwPause=i+1; */
}
if (ack & HPI_HIF_ERROR_MASK)
/* indicates bad read from DSP -
typically 0xffffff is read for some reason */
ack = HPI_HIF_ERROR_MASK;
if (timeout == 0)
ack = HPI_HIF_ERROR_MASK;
return (short)ack;
}
static short hpi6000_update_control_cache(struct hpi_adapter_obj *pao,
struct hpi_message *phm)
{
const u16 dsp_index = 0;
struct hpi_hw_obj *phw = (struct hpi_hw_obj *)pao->priv;
struct dsp_obj *pdo = &phw->ado[dsp_index];
u32 timeout;
u32 cache_dirty_flag;
u16 err;
hpios_dsplock_lock(pao);
timeout = TIMEOUT;
do {
cache_dirty_flag =
hpi_read_word((struct dsp_obj *)pdo,
HPI_HIF_ADDR(control_cache_is_dirty));
} while (hpi6000_check_PCI2040_error_flag(pao, H6READ) && --timeout);
if (!timeout) {
err = HPI6000_ERROR_CONTROL_CACHE_PARAMS;
goto unlock;
}
if (cache_dirty_flag) {
/* read the cached controls */
u32 address;
u32 length;
timeout = TIMEOUT;
if (pdo->control_cache_address_on_dsp == 0) {
do {
address =
hpi_read_word((struct dsp_obj *)pdo,
HPI_HIF_ADDR(control_cache_address));
length = hpi_read_word((struct dsp_obj *)pdo,
HPI_HIF_ADDR
(control_cache_size_in_bytes));
} while (hpi6000_check_PCI2040_error_flag(pao, H6READ)
&& --timeout);
if (!timeout) {
err = HPI6000_ERROR_CONTROL_CACHE_ADDRLEN;
goto unlock;
}
pdo->control_cache_address_on_dsp = address;
pdo->control_cache_length_on_dsp = length;
} else {
address = pdo->control_cache_address_on_dsp;
length = pdo->control_cache_length_on_dsp;
}
if (hpi6000_dsp_block_read32(pao, dsp_index, address,
(u32 *)&phw->control_cache[0],
length / sizeof(u32))) {
err = HPI6000_ERROR_CONTROL_CACHE_READ;
goto unlock;
}
do {
hpi_write_word((struct dsp_obj *)pdo,
HPI_HIF_ADDR(control_cache_is_dirty), 0);
/* flush the FIFO */
hpi_set_address(pdo, HPI_HIF_ADDR(host_cmd));
} while (hpi6000_check_PCI2040_error_flag(pao, H6WRITE)
&& --timeout);
if (!timeout) {
err = HPI6000_ERROR_CONTROL_CACHE_FLUSH;
goto unlock;
}
}
err = 0;
unlock:
hpios_dsplock_unlock(pao);
return err;
}
/** Get dsp index for multi DSP adapters only */
static u16 get_dsp_index(struct hpi_adapter_obj *pao, struct hpi_message *phm)
{
u16 ret = 0;
switch (phm->object) {
case HPI_OBJ_ISTREAM:
if (phm->obj_index < 2)
ret = 1;
break;
case HPI_OBJ_PROFILE:
ret = phm->obj_index;
break;
default:
break;
}
return ret;
}
/** Complete transaction with DSP
Send message, get response, send or get stream data if any.
*/
static void hw_message(struct hpi_adapter_obj *pao, struct hpi_message *phm,
struct hpi_response *phr)
{
u16 error = 0;
u16 dsp_index = 0;
u16 num_dsp = ((struct hpi_hw_obj *)pao->priv)->num_dsp;
hpios_dsplock_lock(pao);
if (num_dsp < 2)
dsp_index = 0;
else {
dsp_index = get_dsp_index(pao, phm);
/* is this checked on the DSP anyway? */
if ((phm->function == HPI_ISTREAM_GROUP_ADD)
|| (phm->function == HPI_OSTREAM_GROUP_ADD)) {
struct hpi_message hm;
u16 add_index;
hm.obj_index = phm->u.d.u.stream.stream_index;
hm.object = phm->u.d.u.stream.object_type;
add_index = get_dsp_index(pao, &hm);
if (add_index != dsp_index) {
phr->error = HPI_ERROR_NO_INTERDSP_GROUPS;
return;
}
}
}
error = hpi6000_message_response_sequence(pao, dsp_index, phm, phr);
/* maybe an error response */
if (error) {
/* something failed in the HPI/DSP interface */
phr->error = error;
/* just the header of the response is valid */
phr->size = sizeof(struct hpi_response_header);
goto err;
}
if (phr->error != 0) /* something failed in the DSP */
goto err;
switch (phm->function) {
case HPI_OSTREAM_WRITE:
case HPI_ISTREAM_ANC_WRITE:
error = hpi6000_send_data(pao, dsp_index, phm, phr);
break;
case HPI_ISTREAM_READ:
case HPI_OSTREAM_ANC_READ:
error = hpi6000_get_data(pao, dsp_index, phm, phr);
break;
case HPI_ADAPTER_GET_ASSERT:
phr->u.a.adapter_index = 0; /* dsp 0 default */
if (num_dsp == 2) {
if (!phr->u.a.adapter_type) {
/* no assert from dsp 0, check dsp 1 */
error = hpi6000_message_response_sequence(pao,
1, phm, phr);
phr->u.a.adapter_index = 1;
}
}
}
if (error)
phr->error = error;
err:
hpios_dsplock_unlock(pao);
return;
}
/*****************************************************************************
AudioScience HPI driver
Copyright (C) 1997-2010 AudioScience Inc. <support@audioscience.com>
This program is free software; you can redistribute it and/or modify
it under the terms of version 2 of the GNU General Public License as
published by the Free Software Foundation;
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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
Public declarations for DSP Proramming Interface to TI C6701
Shared between hpi6000.c and DSP code
(C) Copyright AudioScience Inc. 1998-2003
******************************************************************************/
#ifndef _HPI6000_H_
#define _HPI6000_H_
#define HPI_NMIXER_CONTROLS 200
/*
* Control caching is always supported in the HPI code.
* The DSP should make sure that dwControlCacheSizeInBytes is initialized to 0
* during boot to make it in-active.
*/
struct hpi_hif_6000 {
u32 host_cmd;
u32 dsp_ack;
u32 address;
u32 length;
u32 message_buffer_address;
u32 response_buffer_address;
u32 dsp_number;
u32 adapter_info;
u32 control_cache_is_dirty;
u32 control_cache_address;
u32 control_cache_size_in_bytes;
u32 control_cache_count;
};
#define HPI_HIF_PACK_ADAPTER_INFO(adapter, version_major, version_minor) \
((adapter << 16) | (version_major << 8) | version_minor)
#define HPI_HIF_ADAPTER_INFO_EXTRACT_ADAPTER(adapterinfo) \
((adapterinfo >> 16) & 0xffff)
#define HPI_HIF_ADAPTER_INFO_EXTRACT_HWVERSION_MAJOR(adapterinfo) \
((adapterinfo >> 8) & 0xff)
#define HPI_HIF_ADAPTER_INFO_EXTRACT_HWVERSION_MINOR(adapterinfo) \
(adapterinfo & 0xff)
/* Command/status exchanged between host and DSP */
#define HPI_HIF_IDLE 0
#define HPI_HIF_SEND_MSG 1
#define HPI_HIF_GET_RESP 2
#define HPI_HIF_DATA_MASK 0x10
#define HPI_HIF_SEND_DATA 0x13
#define HPI_HIF_GET_DATA 0x14
#define HPI_HIF_SEND_DONE 5
#define HPI_HIF_RESET 9
#endif /* _HPI6000_H_ */
/******************************************************************************
AudioScience HPI driver
Copyright (C) 1997-2010 AudioScience Inc. <support@audioscience.com>
This program is free software; you can redistribute it and/or modify
it under the terms of version 2 of the GNU General Public License as
published by the Free Software Foundation;
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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
Hardware Programming Interface (HPI) for AudioScience
ASI50xx, AS51xx, ASI6xxx, ASI87xx ASI89xx series adapters.
These PCI and PCIe bus adapters are based on a
TMS320C6205 PCI bus mastering DSP,
and (except ASI50xx) TI TMS320C6xxx floating point DSP
Exported function:
void HPI_6205(struct hpi_message *phm, struct hpi_response *phr)
(C) Copyright AudioScience Inc. 1998-2010
*******************************************************************************/
#define SOURCEFILE_NAME "hpi6205.c"
#include "hpi_internal.h"
#include "hpimsginit.h"
#include "hpidebug.h"
#include "hpi6205.h"
#include "hpidspcd.h"
#include "hpicmn.h"
/*****************************************************************************/
/* HPI6205 specific error codes */
#define HPI6205_ERROR_BASE 1000
/*#define HPI6205_ERROR_MEM_ALLOC 1001 */
#define HPI6205_ERROR_6205_NO_IRQ 1002
#define HPI6205_ERROR_6205_INIT_FAILED 1003
/*#define HPI6205_ERROR_MISSING_DSPCODE 1004 */
#define HPI6205_ERROR_UNKNOWN_PCI_DEVICE 1005
#define HPI6205_ERROR_6205_REG 1006
#define HPI6205_ERROR_6205_DSPPAGE 1007
#define HPI6205_ERROR_BAD_DSPINDEX 1008
#define HPI6205_ERROR_C6713_HPIC 1009
#define HPI6205_ERROR_C6713_HPIA 1010
#define HPI6205_ERROR_C6713_PLL 1011
#define HPI6205_ERROR_DSP_INTMEM 1012
#define HPI6205_ERROR_DSP_EXTMEM 1013
#define HPI6205_ERROR_DSP_PLD 1014
#define HPI6205_ERROR_MSG_RESP_IDLE_TIMEOUT 1015
#define HPI6205_ERROR_MSG_RESP_TIMEOUT 1016
#define HPI6205_ERROR_6205_EEPROM 1017
#define HPI6205_ERROR_DSP_EMIF 1018
#define hpi6205_error(dsp_index, err) (err)
/*****************************************************************************/
/* for C6205 PCI i/f */
/* Host Status Register (HSR) bitfields */
#define C6205_HSR_INTSRC 0x01
#define C6205_HSR_INTAVAL 0x02
#define C6205_HSR_INTAM 0x04
#define C6205_HSR_CFGERR 0x08
#define C6205_HSR_EEREAD 0x10
/* Host-to-DSP Control Register (HDCR) bitfields */
#define C6205_HDCR_WARMRESET 0x01
#define C6205_HDCR_DSPINT 0x02
#define C6205_HDCR_PCIBOOT 0x04
/* DSP Page Register (DSPP) bitfields, */
/* defines 4 Mbyte page that BAR0 points to */
#define C6205_DSPP_MAP1 0x400
/* BAR0 maps to prefetchable 4 Mbyte memory block set by DSPP.
* BAR1 maps to non-prefetchable 8 Mbyte memory block
* of DSP memory mapped registers (starting at 0x01800000).
* 0x01800000 is hardcoded in the PCI i/f, so that only the offset from this
* needs to be added to the BAR1 base address set in the PCI config reg
*/
#define C6205_BAR1_PCI_IO_OFFSET (0x027FFF0L)
#define C6205_BAR1_HSR (C6205_BAR1_PCI_IO_OFFSET)
#define C6205_BAR1_HDCR (C6205_BAR1_PCI_IO_OFFSET+4)
#define C6205_BAR1_DSPP (C6205_BAR1_PCI_IO_OFFSET+8)
/* used to control LED (revA) and reset C6713 (revB) */
#define C6205_BAR0_TIMER1_CTL (0x01980000L)
/* For first 6713 in CE1 space, using DA17,16,2 */
#define HPICL_ADDR 0x01400000L
#define HPICH_ADDR 0x01400004L
#define HPIAL_ADDR 0x01410000L
#define HPIAH_ADDR 0x01410004L
#define HPIDIL_ADDR 0x01420000L
#define HPIDIH_ADDR 0x01420004L
#define HPIDL_ADDR 0x01430000L
#define HPIDH_ADDR 0x01430004L
#define C6713_EMIF_GCTL 0x01800000
#define C6713_EMIF_CE1 0x01800004
#define C6713_EMIF_CE0 0x01800008
#define C6713_EMIF_CE2 0x01800010
#define C6713_EMIF_CE3 0x01800014
#define C6713_EMIF_SDRAMCTL 0x01800018
#define C6713_EMIF_SDRAMTIMING 0x0180001C
#define C6713_EMIF_SDRAMEXT 0x01800020
struct hpi_hw_obj {
/* PCI registers */
__iomem u32 *prHSR;
__iomem u32 *prHDCR;
__iomem u32 *prDSPP;
u32 dsp_page;
struct consistent_dma_area h_locked_mem;
struct bus_master_interface *p_interface_buffer;
u16 flag_outstream_just_reset[HPI_MAX_STREAMS];
/* a non-NULL handle means there is an HPI allocated buffer */
struct consistent_dma_area instream_host_buffers[HPI_MAX_STREAMS];
struct consistent_dma_area outstream_host_buffers[HPI_MAX_STREAMS];
/* non-zero size means a buffer exists, may be external */
u32 instream_host_buffer_size[HPI_MAX_STREAMS];
u32 outstream_host_buffer_size[HPI_MAX_STREAMS];
struct consistent_dma_area h_control_cache;
struct consistent_dma_area h_async_event_buffer;
/* struct hpi_control_cache_single *pControlCache; */
struct hpi_async_event *p_async_event_buffer;
struct hpi_control_cache *p_cache;
};
/*****************************************************************************/
/* local prototypes */
#define check_before_bbm_copy(status, p_bbm_data, l_first_write, l_second_write)
static int wait_dsp_ack(struct hpi_hw_obj *phw, int state, int timeout_us);
static void send_dsp_command(struct hpi_hw_obj *phw, int cmd);
static u16 adapter_boot_load_dsp(struct hpi_adapter_obj *pao,
u32 *pos_error_code);
static u16 message_response_sequence(struct hpi_adapter_obj *pao,
struct hpi_message *phm, struct hpi_response *phr);
static void hw_message(struct hpi_adapter_obj *pao, struct hpi_message *phm,
struct hpi_response *phr);
#define HPI6205_TIMEOUT 1000000
static void subsys_create_adapter(struct hpi_message *phm,
struct hpi_response *phr);
static void subsys_delete_adapter(struct hpi_message *phm,
struct hpi_response *phr);
static u16 create_adapter_obj(struct hpi_adapter_obj *pao,
u32 *pos_error_code);
static void delete_adapter_obj(struct hpi_adapter_obj *pao);
static void outstream_host_buffer_allocate(struct hpi_adapter_obj *pao,
struct hpi_message *phm, struct hpi_response *phr);
static void outstream_host_buffer_get_info(struct hpi_adapter_obj *pao,
struct hpi_message *phm, struct hpi_response *phr);
static void outstream_host_buffer_free(struct hpi_adapter_obj *pao,
struct hpi_message *phm, struct hpi_response *phr);
static void outstream_write(struct hpi_adapter_obj *pao,
struct hpi_message *phm, struct hpi_response *phr);
static void outstream_get_info(struct hpi_adapter_obj *pao,
struct hpi_message *phm, struct hpi_response *phr);
static void outstream_start(struct hpi_adapter_obj *pao,
struct hpi_message *phm, struct hpi_response *phr);
static void outstream_open(struct hpi_adapter_obj *pao,
struct hpi_message *phm, struct hpi_response *phr);
static void outstream_reset(struct hpi_adapter_obj *pao,
struct hpi_message *phm, struct hpi_response *phr);
static void instream_host_buffer_allocate(struct hpi_adapter_obj *pao,
struct hpi_message *phm, struct hpi_response *phr);
static void instream_host_buffer_get_info(struct hpi_adapter_obj *pao,
struct hpi_message *phm, struct hpi_response *phr);
static void instream_host_buffer_free(struct hpi_adapter_obj *pao,
struct hpi_message *phm, struct hpi_response *phr);
static void instream_read(struct hpi_adapter_obj *pao,
struct hpi_message *phm, struct hpi_response *phr);
static void instream_get_info(struct hpi_adapter_obj *pao,
struct hpi_message *phm, struct hpi_response *phr);
static void instream_start(struct hpi_adapter_obj *pao,
struct hpi_message *phm, struct hpi_response *phr);
static u32 boot_loader_read_mem32(struct hpi_adapter_obj *pao, int dsp_index,
u32 address);
static u16 boot_loader_write_mem32(struct hpi_adapter_obj *pao, int dsp_index,
u32 address, u32 data);
static u16 boot_loader_config_emif(struct hpi_adapter_obj *pao,
int dsp_index);
static u16 boot_loader_test_memory(struct hpi_adapter_obj *pao, int dsp_index,
u32 address, u32 length);
static u16 boot_loader_test_internal_memory(struct hpi_adapter_obj *pao,
int dsp_index);
static u16 boot_loader_test_external_memory(struct hpi_adapter_obj *pao,
int dsp_index);
static u16 boot_loader_test_pld(struct hpi_adapter_obj *pao, int dsp_index);
/*****************************************************************************/
static void subsys_message(struct hpi_message *phm, struct hpi_response *phr)
{
switch (phm->function) {
case HPI_SUBSYS_OPEN:
case HPI_SUBSYS_CLOSE:
case HPI_SUBSYS_GET_INFO:
case HPI_SUBSYS_DRIVER_UNLOAD:
case HPI_SUBSYS_DRIVER_LOAD:
case HPI_SUBSYS_FIND_ADAPTERS:
/* messages that should not get here */
phr->error = HPI_ERROR_UNIMPLEMENTED;
break;
case HPI_SUBSYS_CREATE_ADAPTER:
subsys_create_adapter(phm, phr);
break;
case HPI_SUBSYS_DELETE_ADAPTER:
subsys_delete_adapter(phm, phr);
break;
default:
phr->error = HPI_ERROR_INVALID_FUNC;
break;
}
}
static void control_message(struct hpi_adapter_obj *pao,
struct hpi_message *phm, struct hpi_response *phr)
{
struct hpi_hw_obj *phw = pao->priv;
switch (phm->function) {
case HPI_CONTROL_GET_STATE:
if (pao->has_control_cache) {
rmb(); /* make sure we see updates DM_aed from DSP */
if (hpi_check_control_cache(phw->p_cache, phm, phr))
break;
}
hw_message(pao, phm, phr);
break;
case HPI_CONTROL_GET_INFO:
hw_message(pao, phm, phr);
break;
case HPI_CONTROL_SET_STATE:
hw_message(pao, phm, phr);
if (pao->has_control_cache)
hpi_sync_control_cache(phw->p_cache, phm, phr);
break;
default:
phr->error = HPI_ERROR_INVALID_FUNC;
break;
}
}
static void adapter_message(struct hpi_adapter_obj *pao,
struct hpi_message *phm, struct hpi_response *phr)
{
switch (phm->function) {
default:
hw_message(pao, phm, phr);
break;
}
}
static void outstream_message(struct hpi_adapter_obj *pao,
struct hpi_message *phm, struct hpi_response *phr)
{
if (phm->obj_index >= HPI_MAX_STREAMS) {
phr->error = HPI_ERROR_INVALID_STREAM;
HPI_DEBUG_LOG(WARNING,
"message referencing invalid stream %d "
"on adapter index %d\n", phm->obj_index,
phm->adapter_index);
return;
}
switch (phm->function) {
case HPI_OSTREAM_WRITE:
outstream_write(pao, phm, phr);
break;
case HPI_OSTREAM_GET_INFO:
outstream_get_info(pao, phm, phr);
break;
case HPI_OSTREAM_HOSTBUFFER_ALLOC:
outstream_host_buffer_allocate(pao, phm, phr);
break;
case HPI_OSTREAM_HOSTBUFFER_GET_INFO:
outstream_host_buffer_get_info(pao, phm, phr);
break;
case HPI_OSTREAM_HOSTBUFFER_FREE:
outstream_host_buffer_free(pao, phm, phr);
break;
case HPI_OSTREAM_START:
outstream_start(pao, phm, phr);
break;
case HPI_OSTREAM_OPEN:
outstream_open(pao, phm, phr);
break;
case HPI_OSTREAM_RESET:
outstream_reset(pao, phm, phr);
break;
default:
hw_message(pao, phm, phr);
break;
}
}
static void instream_message(struct hpi_adapter_obj *pao,
struct hpi_message *phm, struct hpi_response *phr)
{
if (phm->obj_index >= HPI_MAX_STREAMS) {
phr->error = HPI_ERROR_INVALID_STREAM;
HPI_DEBUG_LOG(WARNING,
"message referencing invalid stream %d "
"on adapter index %d\n", phm->obj_index,
phm->adapter_index);
return;
}
switch (phm->function) {
case HPI_ISTREAM_READ:
instream_read(pao, phm, phr);
break;
case HPI_ISTREAM_GET_INFO:
instream_get_info(pao, phm, phr);
break;
case HPI_ISTREAM_HOSTBUFFER_ALLOC:
instream_host_buffer_allocate(pao, phm, phr);
break;
case HPI_ISTREAM_HOSTBUFFER_GET_INFO:
instream_host_buffer_get_info(pao, phm, phr);
break;
case HPI_ISTREAM_HOSTBUFFER_FREE:
instream_host_buffer_free(pao, phm, phr);
break;
case HPI_ISTREAM_START:
instream_start(pao, phm, phr);
break;
default:
hw_message(pao, phm, phr);
break;
}
}
/*****************************************************************************/
/** Entry point to this HPI backend
* All calls to the HPI start here
*/
void HPI_6205(struct hpi_message *phm, struct hpi_response *phr)
{
struct hpi_adapter_obj *pao = NULL;
/* subsytem messages are processed by every HPI.
* All other messages are ignored unless the adapter index matches
* an adapter in the HPI
*/
HPI_DEBUG_LOG(DEBUG, "HPI obj=%d, func=%d\n", phm->object,
phm->function);
/* if Dsp has crashed then do not communicate with it any more */
if (phm->object != HPI_OBJ_SUBSYSTEM) {
pao = hpi_find_adapter(phm->adapter_index);
if (!pao) {
HPI_DEBUG_LOG(DEBUG,
" %d,%d refused, for another HPI?\n",
phm->object, phm->function);
return;
}
if ((pao->dsp_crashed >= 10)
&& (phm->function != HPI_ADAPTER_DEBUG_READ)) {
/* allow last resort debug read even after crash */
hpi_init_response(phr, phm->object, phm->function,
HPI_ERROR_DSP_HARDWARE);
HPI_DEBUG_LOG(WARNING, " %d,%d dsp crashed.\n",
phm->object, phm->function);
return;
}
}
/* Init default response */
if (phm->function != HPI_SUBSYS_CREATE_ADAPTER)
hpi_init_response(phr, phm->object, phm->function,
HPI_ERROR_PROCESSING_MESSAGE);
HPI_DEBUG_LOG(VERBOSE, "start of switch\n");
switch (phm->type) {
case HPI_TYPE_MESSAGE:
switch (phm->object) {
case HPI_OBJ_SUBSYSTEM:
subsys_message(phm, phr);
break;
case HPI_OBJ_ADAPTER:
phr->size =
sizeof(struct hpi_response_header) +
sizeof(struct hpi_adapter_res);
adapter_message(pao, phm, phr);
break;
case HPI_OBJ_CONTROLEX:
case HPI_OBJ_CONTROL:
control_message(pao, phm, phr);
break;
case HPI_OBJ_OSTREAM:
outstream_message(pao, phm, phr);
break;
case HPI_OBJ_ISTREAM:
instream_message(pao, phm, phr);
break;
default:
hw_message(pao, phm, phr);
break;
}
break;
default:
phr->error = HPI_ERROR_INVALID_TYPE;
break;
}
}
/*****************************************************************************/
/* SUBSYSTEM */
/** Create an adapter object and initialise it based on resource information
* passed in in the message
* *** NOTE - you cannot use this function AND the FindAdapters function at the
* same time, the application must use only one of them to get the adapters ***
*/
static void subsys_create_adapter(struct hpi_message *phm,
struct hpi_response *phr)
{
/* create temp adapter obj, because we don't know what index yet */
struct hpi_adapter_obj ao;
u32 os_error_code;
u16 err;
HPI_DEBUG_LOG(DEBUG, " subsys_create_adapter\n");
memset(&ao, 0, sizeof(ao));
/* this HPI only creates adapters for TI/PCI devices */
if (phm->u.s.resource.bus_type != HPI_BUS_PCI)
return;
if (phm->u.s.resource.r.pci->vendor_id != HPI_PCI_VENDOR_ID_TI)
return;
if (phm->u.s.resource.r.pci->device_id != HPI_PCI_DEV_ID_DSP6205)
return;
ao.priv = kmalloc(sizeof(struct hpi_hw_obj), GFP_KERNEL);
if (!ao.priv) {
HPI_DEBUG_LOG(ERROR, "cant get mem for adapter object\n");
phr->error = HPI_ERROR_MEMORY_ALLOC;
return;
}
memset(ao.priv, 0, sizeof(struct hpi_hw_obj));
ao.pci = *phm->u.s.resource.r.pci;
err = create_adapter_obj(&ao, &os_error_code);
if (!err)
err = hpi_add_adapter(&ao);
if (err) {
phr->u.s.data = os_error_code;
delete_adapter_obj(&ao);
phr->error = err;
return;
}
phr->u.s.aw_adapter_list[ao.index] = ao.adapter_type;
phr->u.s.adapter_index = ao.index;
phr->u.s.num_adapters++;
phr->error = 0;
}
/** delete an adapter - required by WDM driver */
static void subsys_delete_adapter(struct hpi_message *phm,
struct hpi_response *phr)
{
struct hpi_adapter_obj *pao;
struct hpi_hw_obj *phw;
pao = hpi_find_adapter(phm->adapter_index);
if (!pao) {
phr->error = HPI_ERROR_INVALID_OBJ_INDEX;
return;
}
phw = (struct hpi_hw_obj *)pao->priv;
/* reset adapter h/w */
/* Reset C6713 #1 */
boot_loader_write_mem32(pao, 0, C6205_BAR0_TIMER1_CTL, 0);
/* reset C6205 */
iowrite32(C6205_HDCR_WARMRESET, phw->prHDCR);
delete_adapter_obj(pao);
phr->error = 0;
}
/** Create adapter object
allocate buffers, bootload DSPs, initialise control cache
*/
static u16 create_adapter_obj(struct hpi_adapter_obj *pao,
u32 *pos_error_code)
{
struct hpi_hw_obj *phw = pao->priv;
struct bus_master_interface *interface;
u32 phys_addr;
#ifndef HPI6205_NO_HSR_POLL
u32 time_out = HPI6205_TIMEOUT;
u32 temp1;
#endif
int i;
u16 err;
/* init error reporting */
pao->dsp_crashed = 0;
for (i = 0; i < HPI_MAX_STREAMS; i++)
phw->flag_outstream_just_reset[i] = 1;
/* The C6205 memory area 1 is 8Mbyte window into DSP registers */
phw->prHSR =
pao->pci.ap_mem_base[1] +
C6205_BAR1_HSR / sizeof(*pao->pci.ap_mem_base[1]);
phw->prHDCR =
pao->pci.ap_mem_base[1] +
C6205_BAR1_HDCR / sizeof(*pao->pci.ap_mem_base[1]);
phw->prDSPP =
pao->pci.ap_mem_base[1] +
C6205_BAR1_DSPP / sizeof(*pao->pci.ap_mem_base[1]);
pao->has_control_cache = 0;
if (hpios_locked_mem_alloc(&phw->h_locked_mem,
sizeof(struct bus_master_interface),
pao->pci.p_os_data))
phw->p_interface_buffer = NULL;
else if (hpios_locked_mem_get_virt_addr(&phw->h_locked_mem,
(void *)&phw->p_interface_buffer))
phw->p_interface_buffer = NULL;
HPI_DEBUG_LOG(DEBUG, "interface buffer address %p\n",
phw->p_interface_buffer);
if (phw->p_interface_buffer) {
memset((void *)phw->p_interface_buffer, 0,
sizeof(struct bus_master_interface));
phw->p_interface_buffer->dsp_ack = H620_HIF_UNKNOWN;
}
err = adapter_boot_load_dsp(pao, pos_error_code);
if (err)
/* no need to clean up as SubSysCreateAdapter */
/* calls DeleteAdapter on error. */
return err;
HPI_DEBUG_LOG(INFO, "load DSP code OK\n");
/* allow boot load even if mem alloc wont work */
if (!phw->p_interface_buffer)
return hpi6205_error(0, HPI_ERROR_MEMORY_ALLOC);
interface = phw->p_interface_buffer;
#ifndef HPI6205_NO_HSR_POLL
/* wait for first interrupt indicating the DSP init is done */
time_out = HPI6205_TIMEOUT * 10;
temp1 = 0;
while (((temp1 & C6205_HSR_INTSRC) == 0) && --time_out)
temp1 = ioread32(phw->prHSR);
if (temp1 & C6205_HSR_INTSRC)
HPI_DEBUG_LOG(INFO,
"interrupt confirming DSP code running OK\n");
else {
HPI_DEBUG_LOG(ERROR,
"timed out waiting for interrupt "
"confirming DSP code running\n");
return hpi6205_error(0, HPI6205_ERROR_6205_NO_IRQ);
}
/* reset the interrupt */
iowrite32(C6205_HSR_INTSRC, phw->prHSR);
#endif
/* make sure the DSP has started ok */
if (!wait_dsp_ack(phw, H620_HIF_RESET, HPI6205_TIMEOUT * 10)) {
HPI_DEBUG_LOG(ERROR, "timed out waiting reset state \n");
return hpi6205_error(0, HPI6205_ERROR_6205_INIT_FAILED);
}
/* Note that *pao, *phw are zeroed after allocation,
* so pointers and flags are NULL by default.
* Allocate bus mastering control cache buffer and tell the DSP about it
*/
if (interface->control_cache.number_of_controls) {
void *p_control_cache_virtual;
err = hpios_locked_mem_alloc(&phw->h_control_cache,
interface->control_cache.size_in_bytes,
pao->pci.p_os_data);
if (!err)
err = hpios_locked_mem_get_virt_addr(&phw->
h_control_cache, &p_control_cache_virtual);
if (!err) {
memset(p_control_cache_virtual, 0,
interface->control_cache.size_in_bytes);
phw->p_cache =
hpi_alloc_control_cache(interface->
control_cache.number_of_controls,
interface->control_cache.size_in_bytes,
(struct hpi_control_cache_info *)
p_control_cache_virtual);
}
if (!err) {
err = hpios_locked_mem_get_phys_addr(&phw->
h_control_cache, &phys_addr);
interface->control_cache.physical_address32 =
phys_addr;
}
if (!err)
pao->has_control_cache = 1;
else {
if (hpios_locked_mem_valid(&phw->h_control_cache))
hpios_locked_mem_free(&phw->h_control_cache);
pao->has_control_cache = 0;
}
}
/* allocate bus mastering async buffer and tell the DSP about it */
if (interface->async_buffer.b.size) {
err = hpios_locked_mem_alloc(&phw->h_async_event_buffer,
interface->async_buffer.b.size *
sizeof(struct hpi_async_event), pao->pci.p_os_data);
if (!err)
err = hpios_locked_mem_get_virt_addr
(&phw->h_async_event_buffer, (void *)
&phw->p_async_event_buffer);
if (!err)
memset((void *)phw->p_async_event_buffer, 0,
interface->async_buffer.b.size *
sizeof(struct hpi_async_event));
if (!err) {
err = hpios_locked_mem_get_phys_addr
(&phw->h_async_event_buffer, &phys_addr);
interface->async_buffer.physical_address32 =
phys_addr;
}
if (err) {
if (hpios_locked_mem_valid(&phw->
h_async_event_buffer)) {
hpios_locked_mem_free
(&phw->h_async_event_buffer);
phw->p_async_event_buffer = NULL;
}
}
}
send_dsp_command(phw, H620_HIF_IDLE);
{
struct hpi_message hM;
struct hpi_response hR;
u32 max_streams;
HPI_DEBUG_LOG(VERBOSE, "init ADAPTER_GET_INFO\n");
memset(&hM, 0, sizeof(hM));
hM.type = HPI_TYPE_MESSAGE;
hM.size = sizeof(hM);
hM.object = HPI_OBJ_ADAPTER;
hM.function = HPI_ADAPTER_GET_INFO;
hM.adapter_index = 0;
memset(&hR, 0, sizeof(hR));
hR.size = sizeof(hR);
err = message_response_sequence(pao, &hM, &hR);
if (err) {
HPI_DEBUG_LOG(ERROR, "message transport error %d\n",
err);
return err;
}
if (hR.error)
return hR.error;
pao->adapter_type = hR.u.a.adapter_type;
pao->index = hR.u.a.adapter_index;
max_streams = hR.u.a.num_outstreams + hR.u.a.num_instreams;
hpios_locked_mem_prepare((max_streams * 6) / 10, max_streams,
65536, pao->pci.p_os_data);
HPI_DEBUG_LOG(VERBOSE,
"got adapter info type %x index %d serial %d\n",
hR.u.a.adapter_type, hR.u.a.adapter_index,
hR.u.a.serial_number);
}
pao->open = 0; /* upon creation the adapter is closed */
HPI_DEBUG_LOG(INFO, "bootload DSP OK\n");
return 0;
}
/** Free memory areas allocated by adapter
* this routine is called from SubSysDeleteAdapter,
* and SubSysCreateAdapter if duplicate index
*/
static void delete_adapter_obj(struct hpi_adapter_obj *pao)
{
struct hpi_hw_obj *phw;
int i;
phw = pao->priv;
if (hpios_locked_mem_valid(&phw->h_async_event_buffer)) {
hpios_locked_mem_free(&phw->h_async_event_buffer);
phw->p_async_event_buffer = NULL;
}
if (hpios_locked_mem_valid(&phw->h_control_cache)) {
hpios_locked_mem_free(&phw->h_control_cache);
hpi_free_control_cache(phw->p_cache);
}
if (hpios_locked_mem_valid(&phw->h_locked_mem)) {
hpios_locked_mem_free(&phw->h_locked_mem);
phw->p_interface_buffer = NULL;
}
for (i = 0; i < HPI_MAX_STREAMS; i++)
if (hpios_locked_mem_valid(&phw->instream_host_buffers[i])) {
hpios_locked_mem_free(&phw->instream_host_buffers[i]);
/*?phw->InStreamHostBuffers[i] = NULL; */
phw->instream_host_buffer_size[i] = 0;
}
for (i = 0; i < HPI_MAX_STREAMS; i++)
if (hpios_locked_mem_valid(&phw->outstream_host_buffers[i])) {
hpios_locked_mem_free(&phw->outstream_host_buffers
[i]);
phw->outstream_host_buffer_size[i] = 0;
}
hpios_locked_mem_unprepare(pao->pci.p_os_data);
hpi_delete_adapter(pao);
kfree(phw);
}
/*****************************************************************************/
/* OutStream Host buffer functions */
/** Allocate or attach buffer for busmastering
*/
static void outstream_host_buffer_allocate(struct hpi_adapter_obj *pao,
struct hpi_message *phm, struct hpi_response *phr)
{
u16 err = 0;
u32 command = phm->u.d.u.buffer.command;
struct hpi_hw_obj *phw = pao->priv;
struct bus_master_interface *interface = phw->p_interface_buffer;
hpi_init_response(phr, phm->object, phm->function, 0);
if (command == HPI_BUFFER_CMD_EXTERNAL
|| command == HPI_BUFFER_CMD_INTERNAL_ALLOC) {
/* ALLOC phase, allocate a buffer with power of 2 size,
get its bus address for PCI bus mastering
*/
phm->u.d.u.buffer.buffer_size =
roundup_pow_of_two(phm->u.d.u.buffer.buffer_size);
/* return old size and allocated size,
so caller can detect change */
phr->u.d.u.stream_info.data_available =
phw->outstream_host_buffer_size[phm->obj_index];
phr->u.d.u.stream_info.buffer_size =
phm->u.d.u.buffer.buffer_size;
if (phw->outstream_host_buffer_size[phm->obj_index] ==
phm->u.d.u.buffer.buffer_size) {
/* Same size, no action required */
return;
}
if (hpios_locked_mem_valid(&phw->outstream_host_buffers[phm->
obj_index]))
hpios_locked_mem_free(&phw->outstream_host_buffers
[phm->obj_index]);
err = hpios_locked_mem_alloc(&phw->outstream_host_buffers
[phm->obj_index], phm->u.d.u.buffer.buffer_size,
pao->pci.p_os_data);
if (err) {
phr->error = HPI_ERROR_INVALID_DATASIZE;
phw->outstream_host_buffer_size[phm->obj_index] = 0;
return;
}
err = hpios_locked_mem_get_phys_addr
(&phw->outstream_host_buffers[phm->obj_index],
&phm->u.d.u.buffer.pci_address);
/* get the phys addr into msg for single call alloc caller
* needs to do this for split alloc (or use the same message)
* return the phy address for split alloc in the respose too
*/
phr->u.d.u.stream_info.auxiliary_data_available =
phm->u.d.u.buffer.pci_address;
if (err) {
hpios_locked_mem_free(&phw->outstream_host_buffers
[phm->obj_index]);
phw->outstream_host_buffer_size[phm->obj_index] = 0;
phr->error = HPI_ERROR_MEMORY_ALLOC;
return;
}
}
if (command == HPI_BUFFER_CMD_EXTERNAL
|| command == HPI_BUFFER_CMD_INTERNAL_GRANTADAPTER) {
/* GRANT phase. Set up the BBM status, tell the DSP about
the buffer so it can start using BBM.
*/
struct hpi_hostbuffer_status *status;
if (phm->u.d.u.buffer.buffer_size & (phm->u.d.u.buffer.
buffer_size - 1)) {
HPI_DEBUG_LOG(ERROR,
"buffer size must be 2^N not %d\n",
phm->u.d.u.buffer.buffer_size);
phr->error = HPI_ERROR_INVALID_DATASIZE;
return;
}
phw->outstream_host_buffer_size[phm->obj_index] =
phm->u.d.u.buffer.buffer_size;
status = &interface->outstream_host_buffer_status[phm->
obj_index];
status->samples_processed = 0;
status->stream_state = HPI_STATE_STOPPED;
status->dSP_index = 0;
status->host_index = status->dSP_index;
status->size_in_bytes = phm->u.d.u.buffer.buffer_size;
hw_message(pao, phm, phr);
if (phr->error
&& hpios_locked_mem_valid(&phw->
outstream_host_buffers[phm->obj_index])) {
hpios_locked_mem_free(&phw->outstream_host_buffers
[phm->obj_index]);
phw->outstream_host_buffer_size[phm->obj_index] = 0;
}
}
}
static void outstream_host_buffer_get_info(struct hpi_adapter_obj *pao,
struct hpi_message *phm, struct hpi_response *phr)
{
struct hpi_hw_obj *phw = pao->priv;
struct bus_master_interface *interface = phw->p_interface_buffer;
struct hpi_hostbuffer_status *status;
u8 *p_bbm_data;
if (hpios_locked_mem_valid(&phw->outstream_host_buffers[phm->
obj_index])) {
if (hpios_locked_mem_get_virt_addr(&phw->
outstream_host_buffers[phm->obj_index],
(void *)&p_bbm_data)) {
phr->error = HPI_ERROR_INVALID_OPERATION;
return;
}
status = &interface->outstream_host_buffer_status[phm->
obj_index];
hpi_init_response(phr, HPI_OBJ_OSTREAM,
HPI_OSTREAM_HOSTBUFFER_GET_INFO, 0);
phr->u.d.u.hostbuffer_info.p_buffer = p_bbm_data;
phr->u.d.u.hostbuffer_info.p_status = status;
} else {
hpi_init_response(phr, HPI_OBJ_OSTREAM,
HPI_OSTREAM_HOSTBUFFER_GET_INFO,
HPI_ERROR_INVALID_OPERATION);
}
}
static void outstream_host_buffer_free(struct hpi_adapter_obj *pao,
struct hpi_message *phm, struct hpi_response *phr)
{
struct hpi_hw_obj *phw = pao->priv;
u32 command = phm->u.d.u.buffer.command;
if (phw->outstream_host_buffer_size[phm->obj_index]) {
if (command == HPI_BUFFER_CMD_EXTERNAL
|| command == HPI_BUFFER_CMD_INTERNAL_REVOKEADAPTER) {
phw->outstream_host_buffer_size[phm->obj_index] = 0;
hw_message(pao, phm, phr);
/* Tell adapter to stop using the host buffer. */
}
if (command == HPI_BUFFER_CMD_EXTERNAL
|| command == HPI_BUFFER_CMD_INTERNAL_FREE)
hpios_locked_mem_free(&phw->outstream_host_buffers
[phm->obj_index]);
}
/* Should HPI_ERROR_INVALID_OPERATION be returned
if no host buffer is allocated? */
else
hpi_init_response(phr, HPI_OBJ_OSTREAM,
HPI_OSTREAM_HOSTBUFFER_FREE, 0);
}
static long outstream_get_space_available(struct hpi_hostbuffer_status
*status)
{
return status->size_in_bytes - ((long)(status->host_index) -
(long)(status->dSP_index));
}
static void outstream_write(struct hpi_adapter_obj *pao,
struct hpi_message *phm, struct hpi_response *phr)
{
struct hpi_hw_obj *phw = pao->priv;
struct bus_master_interface *interface = phw->p_interface_buffer;
struct hpi_hostbuffer_status *status;
long space_available;
if (!phw->outstream_host_buffer_size[phm->obj_index]) {
/* there is no BBM buffer, write via message */
hw_message(pao, phm, phr);
return;
}
hpi_init_response(phr, phm->object, phm->function, 0);
status = &interface->outstream_host_buffer_status[phm->obj_index];
if (phw->flag_outstream_just_reset[phm->obj_index]) {
/* Format can only change after reset. Must tell DSP. */
u16 function = phm->function;
phw->flag_outstream_just_reset[phm->obj_index] = 0;
phm->function = HPI_OSTREAM_SET_FORMAT;
hw_message(pao, phm, phr); /* send the format to the DSP */
phm->function = function;
if (phr->error)
return;
}
#if 1
if (phw->flag_outstream_just_reset[phm->obj_index]) {
/* First OutStremWrite() call following reset will write data to the
adapter's buffers, reducing delay before stream can start
*/
int partial_write = 0;
unsigned int original_size = 0;
/* Send the first buffer to the DSP the old way. */
/* Limit size of first transfer - */
/* expect that this will not usually be triggered. */
if (phm->u.d.u.data.data_size > HPI6205_SIZEOF_DATA) {
partial_write = 1;
original_size = phm->u.d.u.data.data_size;
phm->u.d.u.data.data_size = HPI6205_SIZEOF_DATA;
}
/* write it */
phm->function = HPI_OSTREAM_WRITE;
hw_message(pao, phm, phr);
/* update status information that the DSP would typically
* update (and will update next time the DSP
* buffer update task reads data from the host BBM buffer)
*/
status->auxiliary_data_available = phm->u.d.u.data.data_size;
status->host_index += phm->u.d.u.data.data_size;
status->dSP_index += phm->u.d.u.data.data_size;
/* if we did a full write, we can return from here. */
if (!partial_write)
return;
/* tweak buffer parameters and let the rest of the */
/* buffer land in internal BBM buffer */
phm->u.d.u.data.data_size =
original_size - HPI6205_SIZEOF_DATA;
phm->u.d.u.data.pb_data += HPI6205_SIZEOF_DATA;
}
#endif
space_available = outstream_get_space_available(status);
if (space_available < (long)phm->u.d.u.data.data_size) {
phr->error = HPI_ERROR_INVALID_DATASIZE;
return;
}
/* HostBuffers is used to indicate host buffer is internally allocated.
otherwise, assumed external, data written externally */
if (phm->u.d.u.data.pb_data
&& hpios_locked_mem_valid(&phw->outstream_host_buffers[phm->
obj_index])) {
u8 *p_bbm_data;
long l_first_write;
u8 *p_app_data = (u8 *)phm->u.d.u.data.pb_data;
if (hpios_locked_mem_get_virt_addr(&phw->
outstream_host_buffers[phm->obj_index],
(void *)&p_bbm_data)) {
phr->error = HPI_ERROR_INVALID_OPERATION;
return;
}
/* either all data,
or enough to fit from current to end of BBM buffer */
l_first_write =
min(phm->u.d.u.data.data_size,
status->size_in_bytes -
(status->host_index & (status->size_in_bytes - 1)));
memcpy(p_bbm_data +
(status->host_index & (status->size_in_bytes - 1)),
p_app_data, l_first_write);
/* remaining data if any */
memcpy(p_bbm_data, p_app_data + l_first_write,
phm->u.d.u.data.data_size - l_first_write);
}
status->host_index += phm->u.d.u.data.data_size;
}
static void outstream_get_info(struct hpi_adapter_obj *pao,
struct hpi_message *phm, struct hpi_response *phr)
{
struct hpi_hw_obj *phw = pao->priv;
struct bus_master_interface *interface = phw->p_interface_buffer;
struct hpi_hostbuffer_status *status;
if (!phw->outstream_host_buffer_size[phm->obj_index]) {
hw_message(pao, phm, phr);
return;
}
hpi_init_response(phr, phm->object, phm->function, 0);
status = &interface->outstream_host_buffer_status[phm->obj_index];
phr->u.d.u.stream_info.state = (u16)status->stream_state;
phr->u.d.u.stream_info.samples_transferred =
status->samples_processed;
phr->u.d.u.stream_info.buffer_size = status->size_in_bytes;
phr->u.d.u.stream_info.data_available =
status->size_in_bytes - outstream_get_space_available(status);
phr->u.d.u.stream_info.auxiliary_data_available =
status->auxiliary_data_available;
}
static void outstream_start(struct hpi_adapter_obj *pao,
struct hpi_message *phm, struct hpi_response *phr)
{
hw_message(pao, phm, phr);
}
static void outstream_reset(struct hpi_adapter_obj *pao,
struct hpi_message *phm, struct hpi_response *phr)
{
struct hpi_hw_obj *phw = pao->priv;
phw->flag_outstream_just_reset[phm->obj_index] = 1;
hw_message(pao, phm, phr);
}
static void outstream_open(struct hpi_adapter_obj *pao,
struct hpi_message *phm, struct hpi_response *phr)
{
outstream_reset(pao, phm, phr);
}
/*****************************************************************************/
/* InStream Host buffer functions */
static void instream_host_buffer_allocate(struct hpi_adapter_obj *pao,
struct hpi_message *phm, struct hpi_response *phr)
{
u16 err = 0;
u32 command = phm->u.d.u.buffer.command;
struct hpi_hw_obj *phw = pao->priv;
struct bus_master_interface *interface = phw->p_interface_buffer;
hpi_init_response(phr, phm->object, phm->function, 0);
if (command == HPI_BUFFER_CMD_EXTERNAL
|| command == HPI_BUFFER_CMD_INTERNAL_ALLOC) {
phm->u.d.u.buffer.buffer_size =
roundup_pow_of_two(phm->u.d.u.buffer.buffer_size);
phr->u.d.u.stream_info.data_available =
phw->instream_host_buffer_size[phm->obj_index];
phr->u.d.u.stream_info.buffer_size =
phm->u.d.u.buffer.buffer_size;
if (phw->instream_host_buffer_size[phm->obj_index] ==
phm->u.d.u.buffer.buffer_size) {
/* Same size, no action required */
return;
}
if (hpios_locked_mem_valid(&phw->instream_host_buffers[phm->
obj_index]))
hpios_locked_mem_free(&phw->instream_host_buffers
[phm->obj_index]);
err = hpios_locked_mem_alloc(&phw->instream_host_buffers[phm->
obj_index], phm->u.d.u.buffer.buffer_size,
pao->pci.p_os_data);
if (err) {
phr->error = HPI_ERROR_INVALID_DATASIZE;
phw->instream_host_buffer_size[phm->obj_index] = 0;
return;
}
err = hpios_locked_mem_get_phys_addr
(&phw->instream_host_buffers[phm->obj_index],
&phm->u.d.u.buffer.pci_address);
/* get the phys addr into msg for single call alloc. Caller
needs to do this for split alloc so return the phy address */
phr->u.d.u.stream_info.auxiliary_data_available =
phm->u.d.u.buffer.pci_address;
if (err) {
hpios_locked_mem_free(&phw->instream_host_buffers
[phm->obj_index]);
phw->instream_host_buffer_size[phm->obj_index] = 0;
phr->error = HPI_ERROR_MEMORY_ALLOC;
return;
}
}
if (command == HPI_BUFFER_CMD_EXTERNAL
|| command == HPI_BUFFER_CMD_INTERNAL_GRANTADAPTER) {
struct hpi_hostbuffer_status *status;
if (phm->u.d.u.buffer.buffer_size & (phm->u.d.u.buffer.
buffer_size - 1)) {
HPI_DEBUG_LOG(ERROR,
"buffer size must be 2^N not %d\n",
phm->u.d.u.buffer.buffer_size);
phr->error = HPI_ERROR_INVALID_DATASIZE;
return;
}
phw->instream_host_buffer_size[phm->obj_index] =
phm->u.d.u.buffer.buffer_size;
status = &interface->instream_host_buffer_status[phm->
obj_index];
status->samples_processed = 0;
status->stream_state = HPI_STATE_STOPPED;
status->dSP_index = 0;
status->host_index = status->dSP_index;
status->size_in_bytes = phm->u.d.u.buffer.buffer_size;
hw_message(pao, phm, phr);
if (phr->error
&& hpios_locked_mem_valid(&phw->
instream_host_buffers[phm->obj_index])) {
hpios_locked_mem_free(&phw->instream_host_buffers
[phm->obj_index]);
phw->instream_host_buffer_size[phm->obj_index] = 0;
}
}
}
static void instream_host_buffer_get_info(struct hpi_adapter_obj *pao,
struct hpi_message *phm, struct hpi_response *phr)
{
struct hpi_hw_obj *phw = pao->priv;
struct bus_master_interface *interface = phw->p_interface_buffer;
struct hpi_hostbuffer_status *status;
u8 *p_bbm_data;
if (hpios_locked_mem_valid(&phw->instream_host_buffers[phm->
obj_index])) {
if (hpios_locked_mem_get_virt_addr(&phw->
instream_host_buffers[phm->obj_index],
(void *)&p_bbm_data)) {
phr->error = HPI_ERROR_INVALID_OPERATION;
return;
}
status = &interface->instream_host_buffer_status[phm->
obj_index];
hpi_init_response(phr, HPI_OBJ_ISTREAM,
HPI_ISTREAM_HOSTBUFFER_GET_INFO, 0);
phr->u.d.u.hostbuffer_info.p_buffer = p_bbm_data;
phr->u.d.u.hostbuffer_info.p_status = status;
} else {
hpi_init_response(phr, HPI_OBJ_ISTREAM,
HPI_ISTREAM_HOSTBUFFER_GET_INFO,
HPI_ERROR_INVALID_OPERATION);
}
}
static void instream_host_buffer_free(struct hpi_adapter_obj *pao,
struct hpi_message *phm, struct hpi_response *phr)
{
struct hpi_hw_obj *phw = pao->priv;
u32 command = phm->u.d.u.buffer.command;
if (phw->instream_host_buffer_size[phm->obj_index]) {
if (command == HPI_BUFFER_CMD_EXTERNAL
|| command == HPI_BUFFER_CMD_INTERNAL_REVOKEADAPTER) {
phw->instream_host_buffer_size[phm->obj_index] = 0;
hw_message(pao, phm, phr);
}
if (command == HPI_BUFFER_CMD_EXTERNAL
|| command == HPI_BUFFER_CMD_INTERNAL_FREE)
hpios_locked_mem_free(&phw->instream_host_buffers
[phm->obj_index]);
} else {
/* Should HPI_ERROR_INVALID_OPERATION be returned
if no host buffer is allocated? */
hpi_init_response(phr, HPI_OBJ_ISTREAM,
HPI_ISTREAM_HOSTBUFFER_FREE, 0);
}
}
static void instream_start(struct hpi_adapter_obj *pao,
struct hpi_message *phm, struct hpi_response *phr)
{
hw_message(pao, phm, phr);
}
static long instream_get_bytes_available(struct hpi_hostbuffer_status *status)
{
return (long)(status->dSP_index) - (long)(status->host_index);
}
static void instream_read(struct hpi_adapter_obj *pao,
struct hpi_message *phm, struct hpi_response *phr)
{
struct hpi_hw_obj *phw = pao->priv;
struct bus_master_interface *interface = phw->p_interface_buffer;
struct hpi_hostbuffer_status *status;
long data_available;
u8 *p_bbm_data;
long l_first_read;
u8 *p_app_data = (u8 *)phm->u.d.u.data.pb_data;
if (!phw->instream_host_buffer_size[phm->obj_index]) {
hw_message(pao, phm, phr);
return;
}
hpi_init_response(phr, phm->object, phm->function, 0);
status = &interface->instream_host_buffer_status[phm->obj_index];
data_available = instream_get_bytes_available(status);
if (data_available < (long)phm->u.d.u.data.data_size) {
phr->error = HPI_ERROR_INVALID_DATASIZE;
return;
}
if (hpios_locked_mem_valid(&phw->instream_host_buffers[phm->
obj_index])) {
if (hpios_locked_mem_get_virt_addr(&phw->
instream_host_buffers[phm->obj_index],
(void *)&p_bbm_data)) {
phr->error = HPI_ERROR_INVALID_OPERATION;
return;
}
/* either all data,
or enough to fit from current to end of BBM buffer */
l_first_read =
min(phm->u.d.u.data.data_size,
status->size_in_bytes -
(status->host_index & (status->size_in_bytes - 1)));
memcpy(p_app_data,
p_bbm_data +
(status->host_index & (status->size_in_bytes - 1)),
l_first_read);
/* remaining data if any */
memcpy(p_app_data + l_first_read, p_bbm_data,
phm->u.d.u.data.data_size - l_first_read);
}
status->host_index += phm->u.d.u.data.data_size;
}
static void instream_get_info(struct hpi_adapter_obj *pao,
struct hpi_message *phm, struct hpi_response *phr)
{
struct hpi_hw_obj *phw = pao->priv;
struct bus_master_interface *interface = phw->p_interface_buffer;
struct hpi_hostbuffer_status *status;
if (!phw->instream_host_buffer_size[phm->obj_index]) {
hw_message(pao, phm, phr);
return;
}
status = &interface->instream_host_buffer_status[phm->obj_index];
hpi_init_response(phr, phm->object, phm->function, 0);
phr->u.d.u.stream_info.state = (u16)status->stream_state;
phr->u.d.u.stream_info.samples_transferred =
status->samples_processed;
phr->u.d.u.stream_info.buffer_size = status->size_in_bytes;
phr->u.d.u.stream_info.data_available =
instream_get_bytes_available(status);
phr->u.d.u.stream_info.auxiliary_data_available =
status->auxiliary_data_available;
}
/*****************************************************************************/
/* LOW-LEVEL */
#define HPI6205_MAX_FILES_TO_LOAD 2
static u16 adapter_boot_load_dsp(struct hpi_adapter_obj *pao,
u32 *pos_error_code)
{
struct hpi_hw_obj *phw = pao->priv;
struct dsp_code dsp_code;
u16 boot_code_id[HPI6205_MAX_FILES_TO_LOAD];
u16 firmware_id = pao->pci.subsys_device_id;
u32 temp;
int dsp = 0, i = 0;
u16 err = 0;
boot_code_id[0] = HPI_ADAPTER_ASI(0x6205);
/* special cases where firmware_id != subsys ID */
switch (firmware_id) {
case HPI_ADAPTER_FAMILY_ASI(0x5000):
boot_code_id[0] = firmware_id;
firmware_id = 0;
break;
case HPI_ADAPTER_FAMILY_ASI(0x5300):
case HPI_ADAPTER_FAMILY_ASI(0x5400):
case HPI_ADAPTER_FAMILY_ASI(0x6300):
firmware_id = HPI_ADAPTER_FAMILY_ASI(0x6400);
break;
case HPI_ADAPTER_FAMILY_ASI(0x5600):
case HPI_ADAPTER_FAMILY_ASI(0x6500):
firmware_id = HPI_ADAPTER_FAMILY_ASI(0x6600);
break;
}
boot_code_id[1] = firmware_id;
/* reset DSP by writing a 1 to the WARMRESET bit */
temp = C6205_HDCR_WARMRESET;
iowrite32(temp, phw->prHDCR);
hpios_delay_micro_seconds(1000);
/* check that PCI i/f was configured by EEPROM */
temp = ioread32(phw->prHSR);
if ((temp & (C6205_HSR_CFGERR | C6205_HSR_EEREAD)) !=
C6205_HSR_EEREAD)
return hpi6205_error(0, HPI6205_ERROR_6205_EEPROM);
temp |= 0x04;
/* disable PINTA interrupt */
iowrite32(temp, phw->prHSR);
/* check control register reports PCI boot mode */
temp = ioread32(phw->prHDCR);
if (!(temp & C6205_HDCR_PCIBOOT))
return hpi6205_error(0, HPI6205_ERROR_6205_REG);
/* try writing a couple of numbers to the DSP page register */
/* and reading them back. */
temp = 1;
iowrite32(temp, phw->prDSPP);
if ((temp | C6205_DSPP_MAP1) != ioread32(phw->prDSPP))
return hpi6205_error(0, HPI6205_ERROR_6205_DSPPAGE);
temp = 2;
iowrite32(temp, phw->prDSPP);
if ((temp | C6205_DSPP_MAP1) != ioread32(phw->prDSPP))
return hpi6205_error(0, HPI6205_ERROR_6205_DSPPAGE);
temp = 3;
iowrite32(temp, phw->prDSPP);
if ((temp | C6205_DSPP_MAP1) != ioread32(phw->prDSPP))
return hpi6205_error(0, HPI6205_ERROR_6205_DSPPAGE);
/* reset DSP page to the correct number */
temp = 0;
iowrite32(temp, phw->prDSPP);
if ((temp | C6205_DSPP_MAP1) != ioread32(phw->prDSPP))
return hpi6205_error(0, HPI6205_ERROR_6205_DSPPAGE);
phw->dsp_page = 0;
/* release 6713 from reset before 6205 is bootloaded.
This ensures that the EMIF is inactive,
and the 6713 HPI gets the correct bootmode etc
*/
if (boot_code_id[1] != 0) {
/* DSP 1 is a C6713 */
/* CLKX0 <- '1' release the C6205 bootmode pulldowns */
boot_loader_write_mem32(pao, 0, (0x018C0024L), 0x00002202);
hpios_delay_micro_seconds(100);
/* Reset the 6713 #1 - revB */
boot_loader_write_mem32(pao, 0, C6205_BAR0_TIMER1_CTL, 0);
/* dummy read every 4 words for 6205 advisory 1.4.4 */
boot_loader_read_mem32(pao, 0, 0);
hpios_delay_micro_seconds(100);
/* Release C6713 from reset - revB */
boot_loader_write_mem32(pao, 0, C6205_BAR0_TIMER1_CTL, 4);
hpios_delay_micro_seconds(100);
}
for (dsp = 0; dsp < HPI6205_MAX_FILES_TO_LOAD; dsp++) {
/* is there a DSP to load? */
if (boot_code_id[dsp] == 0)
continue;
err = boot_loader_config_emif(pao, dsp);
if (err)
return err;
err = boot_loader_test_internal_memory(pao, dsp);
if (err)
return err;
err = boot_loader_test_external_memory(pao, dsp);
if (err)
return err;
err = boot_loader_test_pld(pao, dsp);
if (err)
return err;
/* write the DSP code down into the DSPs memory */
dsp_code.ps_dev = pao->pci.p_os_data;
err = hpi_dsp_code_open(boot_code_id[dsp], &dsp_code,
pos_error_code);
if (err)
return err;
while (1) {
u32 length;
u32 address;
u32 type;
u32 *pcode;
err = hpi_dsp_code_read_word(&dsp_code, &length);
if (err)
break;
if (length == 0xFFFFFFFF)
break; /* end of code */
err = hpi_dsp_code_read_word(&dsp_code, &address);
if (err)
break;
err = hpi_dsp_code_read_word(&dsp_code, &type);
if (err)
break;
err = hpi_dsp_code_read_block(length, &dsp_code,
&pcode);
if (err)
break;
for (i = 0; i < (int)length; i++) {
err = boot_loader_write_mem32(pao, dsp,
address, *pcode);
if (err)
break;
/* dummy read every 4 words */
/* for 6205 advisory 1.4.4 */
if (i % 4 == 0)
boot_loader_read_mem32(pao, dsp,
address);
pcode++;
address += 4;
}
}
if (err) {
hpi_dsp_code_close(&dsp_code);
return err;
}
/* verify code */
hpi_dsp_code_rewind(&dsp_code);
while (1) {
u32 length = 0;
u32 address = 0;
u32 type = 0;
u32 *pcode = NULL;
u32 data = 0;
hpi_dsp_code_read_word(&dsp_code, &length);
if (length == 0xFFFFFFFF)
break; /* end of code */
hpi_dsp_code_read_word(&dsp_code, &address);
hpi_dsp_code_read_word(&dsp_code, &type);
hpi_dsp_code_read_block(length, &dsp_code, &pcode);
for (i = 0; i < (int)length; i++) {
data = boot_loader_read_mem32(pao, dsp,
address);
if (data != *pcode) {
err = 0;
break;
}
pcode++;
address += 4;
}
if (err)
break;
}
hpi_dsp_code_close(&dsp_code);
if (err)
return err;
}
/* After bootloading all DSPs, start DSP0 running
* The DSP0 code will handle starting and synchronizing with its slaves
*/
if (phw->p_interface_buffer) {
/* we need to tell the card the physical PCI address */
u32 physicalPC_iaddress;
struct bus_master_interface *interface =
phw->p_interface_buffer;
u32 host_mailbox_address_on_dsp;
u32 physicalPC_iaddress_verify = 0;
int time_out = 10;
/* set ack so we know when DSP is ready to go */
/* (dwDspAck will be changed to HIF_RESET) */
interface->dsp_ack = H620_HIF_UNKNOWN;
wmb(); /* ensure ack is written before dsp writes back */
err = hpios_locked_mem_get_phys_addr(&phw->h_locked_mem,
&physicalPC_iaddress);
/* locate the host mailbox on the DSP. */
host_mailbox_address_on_dsp = 0x80000000;
while ((physicalPC_iaddress != physicalPC_iaddress_verify)
&& time_out--) {
err = boot_loader_write_mem32(pao, 0,
host_mailbox_address_on_dsp,
physicalPC_iaddress);
physicalPC_iaddress_verify =
boot_loader_read_mem32(pao, 0,
host_mailbox_address_on_dsp);
}
}
HPI_DEBUG_LOG(DEBUG, "starting DS_ps running\n");
/* enable interrupts */
temp = ioread32(phw->prHSR);
temp &= ~(u32)C6205_HSR_INTAM;
iowrite32(temp, phw->prHSR);
/* start code running... */
temp = ioread32(phw->prHDCR);
temp |= (u32)C6205_HDCR_DSPINT;
iowrite32(temp, phw->prHDCR);
/* give the DSP 10ms to start up */
hpios_delay_micro_seconds(10000);
return err;
}
/*****************************************************************************/
/* Bootloader utility functions */
static u32 boot_loader_read_mem32(struct hpi_adapter_obj *pao, int dsp_index,
u32 address)
{
struct hpi_hw_obj *phw = pao->priv;
u32 data = 0;
__iomem u32 *p_data;
if (dsp_index == 0) {
/* DSP 0 is always C6205 */
if ((address >= 0x01800000) & (address < 0x02000000)) {
/* BAR1 register access */
p_data = pao->pci.ap_mem_base[1] +
(address & 0x007fffff) /
sizeof(*pao->pci.ap_mem_base[1]);
/* HPI_DEBUG_LOG(WARNING,
"BAR1 access %08x\n", dwAddress); */
} else {
u32 dw4M_page = address >> 22L;
if (dw4M_page != phw->dsp_page) {
phw->dsp_page = dw4M_page;
/* *INDENT OFF* */
iowrite32(phw->dsp_page, phw->prDSPP);
/* *INDENT-ON* */
}
address &= 0x3fffff; /* address within 4M page */
/* BAR0 memory access */
p_data = pao->pci.ap_mem_base[0] +
address / sizeof(u32);
}
data = ioread32(p_data);
} else if (dsp_index == 1) {
/* DSP 1 is a C6713 */
u32 lsb;
boot_loader_write_mem32(pao, 0, HPIAL_ADDR, address);
boot_loader_write_mem32(pao, 0, HPIAH_ADDR, address >> 16);
lsb = boot_loader_read_mem32(pao, 0, HPIDL_ADDR);
data = boot_loader_read_mem32(pao, 0, HPIDH_ADDR);
data = (data << 16) | (lsb & 0xFFFF);
}
return data;
}
static u16 boot_loader_write_mem32(struct hpi_adapter_obj *pao, int dsp_index,
u32 address, u32 data)
{
struct hpi_hw_obj *phw = pao->priv;
u16 err = 0;
__iomem u32 *p_data;
/* u32 dwVerifyData=0; */
if (dsp_index == 0) {
/* DSP 0 is always C6205 */
if ((address >= 0x01800000) & (address < 0x02000000)) {
/* BAR1 - DSP register access using */
/* Non-prefetchable PCI access */
p_data = pao->pci.ap_mem_base[1] +
(address & 0x007fffff) /
sizeof(*pao->pci.ap_mem_base[1]);
} else {
/* BAR0 access - all of DSP memory using */
/* pre-fetchable PCI access */
u32 dw4M_page = address >> 22L;
if (dw4M_page != phw->dsp_page) {
phw->dsp_page = dw4M_page;
/* *INDENT-OFF* */
iowrite32(phw->dsp_page, phw->prDSPP);
/* *INDENT-ON* */
}
address &= 0x3fffff; /* address within 4M page */
p_data = pao->pci.ap_mem_base[0] +
address / sizeof(u32);
}
iowrite32(data, p_data);
} else if (dsp_index == 1) {
/* DSP 1 is a C6713 */
boot_loader_write_mem32(pao, 0, HPIAL_ADDR, address);
boot_loader_write_mem32(pao, 0, HPIAH_ADDR, address >> 16);
/* dummy read every 4 words for 6205 advisory 1.4.4 */
boot_loader_read_mem32(pao, 0, 0);
boot_loader_write_mem32(pao, 0, HPIDL_ADDR, data);
boot_loader_write_mem32(pao, 0, HPIDH_ADDR, data >> 16);
/* dummy read every 4 words for 6205 advisory 1.4.4 */
boot_loader_read_mem32(pao, 0, 0);
} else
err = hpi6205_error(dsp_index, HPI6205_ERROR_BAD_DSPINDEX);
return err;
}
static u16 boot_loader_config_emif(struct hpi_adapter_obj *pao, int dsp_index)
{
u16 err = 0;
if (dsp_index == 0) {
u32 setting;
/* DSP 0 is always C6205 */
/* Set the EMIF */
/* memory map of C6205 */
/* 00000000-0000FFFF 16Kx32 internal program */
/* 00400000-00BFFFFF CE0 2Mx32 SDRAM running @ 100MHz */
/* EMIF config */
/*------------ */
/* Global EMIF control */
boot_loader_write_mem32(pao, dsp_index, 0x01800000, 0x3779);
#define WS_OFS 28
#define WST_OFS 22
#define WH_OFS 20
#define RS_OFS 16
#define RST_OFS 8
#define MTYPE_OFS 4
#define RH_OFS 0
/* EMIF CE0 setup - 2Mx32 Sync DRAM on ASI5000 cards only */
setting = 0x00000030;
boot_loader_write_mem32(pao, dsp_index, 0x01800008, setting);
if (setting != boot_loader_read_mem32(pao, dsp_index,
0x01800008))
return hpi6205_error(dsp_index,
HPI6205_ERROR_DSP_EMIF);
/* EMIF CE1 setup - 32 bit async. This is 6713 #1 HPI, */
/* which occupies D15..0. 6713 starts at 27MHz, so need */
/* plenty of wait states. See dsn8701.rtf, and 6713 errata. */
/* WST should be 71, but 63 is max possible */
setting =
(1L << WS_OFS) | (63L << WST_OFS) | (1L << WH_OFS) |
(1L << RS_OFS) | (63L << RST_OFS) | (1L << RH_OFS) |
(2L << MTYPE_OFS);
boot_loader_write_mem32(pao, dsp_index, 0x01800004, setting);
if (setting != boot_loader_read_mem32(pao, dsp_index,
0x01800004))
return hpi6205_error(dsp_index,
HPI6205_ERROR_DSP_EMIF);
/* EMIF CE2 setup - 32 bit async. This is 6713 #2 HPI, */
/* which occupies D15..0. 6713 starts at 27MHz, so need */
/* plenty of wait states */
setting =
(1L << WS_OFS) | (28L << WST_OFS) | (1L << WH_OFS) |
(1L << RS_OFS) | (63L << RST_OFS) | (1L << RH_OFS) |
(2L << MTYPE_OFS);
boot_loader_write_mem32(pao, dsp_index, 0x01800010, setting);
if (setting != boot_loader_read_mem32(pao, dsp_index,
0x01800010))
return hpi6205_error(dsp_index,
HPI6205_ERROR_DSP_EMIF);
/* EMIF CE3 setup - 32 bit async. */
/* This is the PLD on the ASI5000 cards only */
setting =
(1L << WS_OFS) | (10L << WST_OFS) | (1L << WH_OFS) |
(1L << RS_OFS) | (10L << RST_OFS) | (1L << RH_OFS) |
(2L << MTYPE_OFS);
boot_loader_write_mem32(pao, dsp_index, 0x01800014, setting);
if (setting != boot_loader_read_mem32(pao, dsp_index,
0x01800014))
return hpi6205_error(dsp_index,
HPI6205_ERROR_DSP_EMIF);
/* set EMIF SDRAM control for 2Mx32 SDRAM (512x32x4 bank) */
/* need to use this else DSP code crashes? */
boot_loader_write_mem32(pao, dsp_index, 0x01800018,
0x07117000);
/* EMIF SDRAM Refresh Timing */
/* EMIF SDRAM timing (orig = 0x410, emulator = 0x61a) */
boot_loader_write_mem32(pao, dsp_index, 0x0180001C,
0x00000410);
} else if (dsp_index == 1) {
/* test access to the C6713s HPI registers */
u32 write_data = 0, read_data = 0, i = 0;
/* Set up HPIC for little endian, by setiing HPIC:HWOB=1 */
write_data = 1;
boot_loader_write_mem32(pao, 0, HPICL_ADDR, write_data);
boot_loader_write_mem32(pao, 0, HPICH_ADDR, write_data);
/* C67 HPI is on lower 16bits of 32bit EMIF */
read_data =
0xFFF7 & boot_loader_read_mem32(pao, 0, HPICL_ADDR);
if (write_data != read_data) {
err = hpi6205_error(dsp_index,
HPI6205_ERROR_C6713_HPIC);
HPI_DEBUG_LOG(ERROR, "HPICL %x %x\n", write_data,
read_data);
return err;
}
/* HPIA - walking ones test */
write_data = 1;
for (i = 0; i < 32; i++) {
boot_loader_write_mem32(pao, 0, HPIAL_ADDR,
write_data);
boot_loader_write_mem32(pao, 0, HPIAH_ADDR,
(write_data >> 16));
read_data =
0xFFFF & boot_loader_read_mem32(pao, 0,
HPIAL_ADDR);
read_data =
read_data | ((0xFFFF &
boot_loader_read_mem32(pao, 0,
HPIAH_ADDR))
<< 16);
if (read_data != write_data) {
err = hpi6205_error(dsp_index,
HPI6205_ERROR_C6713_HPIA);
HPI_DEBUG_LOG(ERROR, "HPIA %x %x\n",
write_data, read_data);
return err;
}
write_data = write_data << 1;
}
/* setup C67x PLL
* ** C6713 datasheet says we cannot program PLL from HPI,
* and indeed if we try to set the PLL multiply from the HPI,
* the PLL does not seem to lock, so we enable the PLL and
* use the default multiply of x 7, which for a 27MHz clock
* gives a DSP speed of 189MHz
*/
/* bypass PLL */
boot_loader_write_mem32(pao, dsp_index, 0x01B7C100, 0x0000);
hpios_delay_micro_seconds(1000);
/* EMIF = 189/3=63MHz */
boot_loader_write_mem32(pao, dsp_index, 0x01B7C120, 0x8002);
/* peri = 189/2 */
boot_loader_write_mem32(pao, dsp_index, 0x01B7C11C, 0x8001);
/* cpu = 189/1 */
boot_loader_write_mem32(pao, dsp_index, 0x01B7C118, 0x8000);
hpios_delay_micro_seconds(1000);
/* ** SGT test to take GPO3 high when we start the PLL */
/* and low when the delay is completed */
/* FSX0 <- '1' (GPO3) */
boot_loader_write_mem32(pao, 0, (0x018C0024L), 0x00002A0A);
/* PLL not bypassed */
boot_loader_write_mem32(pao, dsp_index, 0x01B7C100, 0x0001);
hpios_delay_micro_seconds(1000);
/* FSX0 <- '0' (GPO3) */
boot_loader_write_mem32(pao, 0, (0x018C0024L), 0x00002A02);
/* 6205 EMIF CE1 resetup - 32 bit async. */
/* Now 6713 #1 is running at 189MHz can reduce waitstates */
boot_loader_write_mem32(pao, 0, 0x01800004, /* CE1 */
(1L << WS_OFS) | (8L << WST_OFS) | (1L << WH_OFS) |
(1L << RS_OFS) | (12L << RST_OFS) | (1L << RH_OFS) |
(2L << MTYPE_OFS));
hpios_delay_micro_seconds(1000);
/* check that we can read one of the PLL registers */
/* PLL should not be bypassed! */
if ((boot_loader_read_mem32(pao, dsp_index, 0x01B7C100) & 0xF)
!= 0x0001) {
err = hpi6205_error(dsp_index,
HPI6205_ERROR_C6713_PLL);
return err;
}
/* setup C67x EMIF (note this is the only use of
BAR1 via BootLoader_WriteMem32) */
boot_loader_write_mem32(pao, dsp_index, C6713_EMIF_GCTL,
0x000034A8);
boot_loader_write_mem32(pao, dsp_index, C6713_EMIF_CE0,
0x00000030);
boot_loader_write_mem32(pao, dsp_index, C6713_EMIF_SDRAMEXT,
0x001BDF29);
boot_loader_write_mem32(pao, dsp_index, C6713_EMIF_SDRAMCTL,
0x47117000);
boot_loader_write_mem32(pao, dsp_index,
C6713_EMIF_SDRAMTIMING, 0x00000410);
hpios_delay_micro_seconds(1000);
} else if (dsp_index == 2) {
/* DSP 2 is a C6713 */
} else
err = hpi6205_error(dsp_index, HPI6205_ERROR_BAD_DSPINDEX);
return err;
}
static u16 boot_loader_test_memory(struct hpi_adapter_obj *pao, int dsp_index,
u32 start_address, u32 length)
{
u32 i = 0, j = 0;
u32 test_addr = 0;
u32 test_data = 0, data = 0;
length = 1000;
/* for 1st word, test each bit in the 32bit word, */
/* dwLength specifies number of 32bit words to test */
/*for(i=0; i<dwLength; i++) */
i = 0;
{
test_addr = start_address + i * 4;
test_data = 0x00000001;
for (j = 0; j < 32; j++) {
boot_loader_write_mem32(pao, dsp_index, test_addr,
test_data);
data = boot_loader_read_mem32(pao, dsp_index,
test_addr);
if (data != test_data) {
HPI_DEBUG_LOG(VERBOSE,
"memtest error details "
"%08x %08x %08x %i\n", test_addr,
test_data, data, dsp_index);
return 1; /* error */
}
test_data = test_data << 1;
} /* for(j) */
} /* for(i) */
/* for the next 100 locations test each location, leaving it as zero */
/* write a zero to the next word in memory before we read */
/* the previous write to make sure every memory location is unique */
for (i = 0; i < 100; i++) {
test_addr = start_address + i * 4;
test_data = 0xA5A55A5A;
boot_loader_write_mem32(pao, dsp_index, test_addr, test_data);
boot_loader_write_mem32(pao, dsp_index, test_addr + 4, 0);
data = boot_loader_read_mem32(pao, dsp_index, test_addr);
if (data != test_data) {
HPI_DEBUG_LOG(VERBOSE,
"memtest error details "
"%08x %08x %08x %i\n", test_addr, test_data,
data, dsp_index);
return 1; /* error */
}
/* leave location as zero */
boot_loader_write_mem32(pao, dsp_index, test_addr, 0x0);
}
/* zero out entire memory block */
for (i = 0; i < length; i++) {
test_addr = start_address + i * 4;
boot_loader_write_mem32(pao, dsp_index, test_addr, 0x0);
}
return 0;
}
static u16 boot_loader_test_internal_memory(struct hpi_adapter_obj *pao,
int dsp_index)
{
int err = 0;
if (dsp_index == 0) {
/* DSP 0 is a C6205 */
/* 64K prog mem */
err = boot_loader_test_memory(pao, dsp_index, 0x00000000,
0x10000);
if (!err)
/* 64K data mem */
err = boot_loader_test_memory(pao, dsp_index,
0x80000000, 0x10000);
} else if ((dsp_index == 1) || (dsp_index == 2)) {
/* DSP 1&2 are a C6713 */
/* 192K internal mem */
err = boot_loader_test_memory(pao, dsp_index, 0x00000000,
0x30000);
if (!err)
/* 64K internal mem / L2 cache */
err = boot_loader_test_memory(pao, dsp_index,
0x00030000, 0x10000);
} else
return hpi6205_error(dsp_index, HPI6205_ERROR_BAD_DSPINDEX);
if (err)
return hpi6205_error(dsp_index, HPI6205_ERROR_DSP_INTMEM);
else
return 0;
}
static u16 boot_loader_test_external_memory(struct hpi_adapter_obj *pao,
int dsp_index)
{
u32 dRAM_start_address = 0;
u32 dRAM_size = 0;
if (dsp_index == 0) {
/* only test for SDRAM if an ASI5000 card */
if (pao->pci.subsys_device_id == 0x5000) {
/* DSP 0 is always C6205 */
dRAM_start_address = 0x00400000;
dRAM_size = 0x200000;
/*dwDRAMinc=1024; */
} else
return 0;
} else if ((dsp_index == 1) || (dsp_index == 2)) {
/* DSP 1 is a C6713 */
dRAM_start_address = 0x80000000;
dRAM_size = 0x200000;
/*dwDRAMinc=1024; */
} else
return hpi6205_error(dsp_index, HPI6205_ERROR_BAD_DSPINDEX);
if (boot_loader_test_memory(pao, dsp_index, dRAM_start_address,
dRAM_size))
return hpi6205_error(dsp_index, HPI6205_ERROR_DSP_EXTMEM);
return 0;
}
static u16 boot_loader_test_pld(struct hpi_adapter_obj *pao, int dsp_index)
{
u32 data = 0;
if (dsp_index == 0) {
/* only test for DSP0 PLD on ASI5000 card */
if (pao->pci.subsys_device_id == 0x5000) {
/* PLD is located at CE3=0x03000000 */
data = boot_loader_read_mem32(pao, dsp_index,
0x03000008);
if ((data & 0xF) != 0x5)
return hpi6205_error(dsp_index,
HPI6205_ERROR_DSP_PLD);
data = boot_loader_read_mem32(pao, dsp_index,
0x0300000C);
if ((data & 0xF) != 0xA)
return hpi6205_error(dsp_index,
HPI6205_ERROR_DSP_PLD);
}
} else if (dsp_index == 1) {
/* DSP 1 is a C6713 */
if (pao->pci.subsys_device_id == 0x8700) {
/* PLD is located at CE1=0x90000000 */
data = boot_loader_read_mem32(pao, dsp_index,
0x90000010);
if ((data & 0xFF) != 0xAA)
return hpi6205_error(dsp_index,
HPI6205_ERROR_DSP_PLD);
/* 8713 - LED on */
boot_loader_write_mem32(pao, dsp_index, 0x90000000,
0x02);
}
}
return 0;
}
/** Transfer data to or from DSP
nOperation = H620_H620_HIF_SEND_DATA or H620_HIF_GET_DATA
*/
static short hpi6205_transfer_data(struct hpi_adapter_obj *pao, u8 *p_data,
u32 data_size, int operation)
{
struct hpi_hw_obj *phw = pao->priv;
u32 data_transferred = 0;
u16 err = 0;
#ifndef HPI6205_NO_HSR_POLL
u32 time_out;
#endif
u32 temp2;
struct bus_master_interface *interface = phw->p_interface_buffer;
if (!p_data)
return HPI_ERROR_INVALID_DATA_TRANSFER;
data_size &= ~3L; /* round data_size down to nearest 4 bytes */
/* make sure state is IDLE */
if (!wait_dsp_ack(phw, H620_HIF_IDLE, HPI6205_TIMEOUT))
return HPI_ERROR_DSP_HARDWARE;
while (data_transferred < data_size) {
u32 this_copy = data_size - data_transferred;
if (this_copy > HPI6205_SIZEOF_DATA)
this_copy = HPI6205_SIZEOF_DATA;
if (operation == H620_HIF_SEND_DATA)
memcpy((void *)&interface->u.b_data[0],
&p_data[data_transferred], this_copy);
interface->transfer_size_in_bytes = this_copy;
#ifdef HPI6205_NO_HSR_POLL
/* DSP must change this back to nOperation */
interface->dsp_ack = H620_HIF_IDLE;
#endif
send_dsp_command(phw, operation);
#ifdef HPI6205_NO_HSR_POLL
temp2 = wait_dsp_ack(phw, operation, HPI6205_TIMEOUT);
HPI_DEBUG_LOG(DEBUG, "spun %d times for data xfer of %d\n",
HPI6205_TIMEOUT - temp2, this_copy);
if (!temp2) {
/* timed out */
HPI_DEBUG_LOG(ERROR,
"timed out waiting for " "state %d got %d\n",
operation, interface->dsp_ack);
break;
}
#else
/* spin waiting on the result */
time_out = HPI6205_TIMEOUT;
temp2 = 0;
while ((temp2 == 0) && time_out--) {
/* give 16k bus mastering transfer time to happen */
/*(16k / 132Mbytes/s = 122usec) */
hpios_delay_micro_seconds(20);
temp2 = ioread32(phw->prHSR);
temp2 &= C6205_HSR_INTSRC;
}
HPI_DEBUG_LOG(DEBUG, "spun %d times for data xfer of %d\n",
HPI6205_TIMEOUT - time_out, this_copy);
if (temp2 == C6205_HSR_INTSRC) {
HPI_DEBUG_LOG(VERBOSE,
"interrupt from HIF <data> OK\n");
/*
if(interface->dwDspAck != nOperation) {
HPI_DEBUG_LOG(DEBUG("interface->dwDspAck=%d,
expected %d \n",
interface->dwDspAck,nOperation);
}
*/
}
/* need to handle this differently... */
else {
HPI_DEBUG_LOG(ERROR,
"interrupt from HIF <data> BAD\n");
err = HPI_ERROR_DSP_HARDWARE;
}
/* reset the interrupt from the DSP */
iowrite32(C6205_HSR_INTSRC, phw->prHSR);
#endif
if (operation == H620_HIF_GET_DATA)
memcpy(&p_data[data_transferred],
(void *)&interface->u.b_data[0], this_copy);
data_transferred += this_copy;
}
if (interface->dsp_ack != operation)
HPI_DEBUG_LOG(DEBUG, "interface->dsp_ack=%d, expected %d\n",
interface->dsp_ack, operation);
/* err=HPI_ERROR_DSP_HARDWARE; */
send_dsp_command(phw, H620_HIF_IDLE);
return err;
}
/* wait for up to timeout_us microseconds for the DSP
to signal state by DMA into dwDspAck
*/
static int wait_dsp_ack(struct hpi_hw_obj *phw, int state, int timeout_us)
{
struct bus_master_interface *interface = phw->p_interface_buffer;
int t = timeout_us / 4;
rmb(); /* ensure interface->dsp_ack is up to date */
while ((interface->dsp_ack != state) && --t) {
hpios_delay_micro_seconds(4);
rmb(); /* DSP changes dsp_ack by DMA */
}
/*HPI_DEBUG_LOG(VERBOSE, "Spun %d for %d\n", timeout_us/4-t, state); */
return t * 4;
}
/* set the busmaster interface to cmd, then interrupt the DSP */
static void send_dsp_command(struct hpi_hw_obj *phw, int cmd)
{
struct bus_master_interface *interface = phw->p_interface_buffer;
u32 r;
interface->host_cmd = cmd;
wmb(); /* DSP gets state by DMA, make sure it is written to memory */
/* before we interrupt the DSP */
r = ioread32(phw->prHDCR);
r |= (u32)C6205_HDCR_DSPINT;
iowrite32(r, phw->prHDCR);
r &= ~(u32)C6205_HDCR_DSPINT;
iowrite32(r, phw->prHDCR);
}
static unsigned int message_count;
static u16 message_response_sequence(struct hpi_adapter_obj *pao,
struct hpi_message *phm, struct hpi_response *phr)
{
#ifndef HPI6205_NO_HSR_POLL
u32 temp2;
#endif
u32 time_out, time_out2;
struct hpi_hw_obj *phw = pao->priv;
struct bus_master_interface *interface = phw->p_interface_buffer;
u16 err = 0;
message_count++;
/* Assume buffer of type struct bus_master_interface
is allocated "noncacheable" */
if (!wait_dsp_ack(phw, H620_HIF_IDLE, HPI6205_TIMEOUT)) {
HPI_DEBUG_LOG(DEBUG, "timeout waiting for idle\n");
return hpi6205_error(0, HPI6205_ERROR_MSG_RESP_IDLE_TIMEOUT);
}
interface->u.message_buffer = *phm;
/* signal we want a response */
send_dsp_command(phw, H620_HIF_GET_RESP);
time_out2 = wait_dsp_ack(phw, H620_HIF_GET_RESP, HPI6205_TIMEOUT);
if (time_out2 == 0) {
HPI_DEBUG_LOG(ERROR,
"(%u) timed out waiting for " "GET_RESP state [%x]\n",
message_count, interface->dsp_ack);
} else {
HPI_DEBUG_LOG(VERBOSE,
"(%u) transition to GET_RESP after %u\n",
message_count, HPI6205_TIMEOUT - time_out2);
}
/* spin waiting on HIF interrupt flag (end of msg process) */
time_out = HPI6205_TIMEOUT;
#ifndef HPI6205_NO_HSR_POLL
temp2 = 0;
while ((temp2 == 0) && --time_out) {
temp2 = ioread32(phw->prHSR);
temp2 &= C6205_HSR_INTSRC;
hpios_delay_micro_seconds(1);
}
if (temp2 == C6205_HSR_INTSRC) {
rmb(); /* ensure we see latest value for dsp_ack */
if ((interface->dsp_ack != H620_HIF_GET_RESP)) {
HPI_DEBUG_LOG(DEBUG,
"(%u)interface->dsp_ack(0x%x) != "
"H620_HIF_GET_RESP, t=%u\n", message_count,
interface->dsp_ack,
HPI6205_TIMEOUT - time_out);
} else {
HPI_DEBUG_LOG(VERBOSE,
"(%u)int with GET_RESP after %u\n",
message_count, HPI6205_TIMEOUT - time_out);
}
} else {
/* can we do anything else in response to the error ? */
HPI_DEBUG_LOG(ERROR,
"interrupt from HIF module BAD (function %x)\n",
phm->function);
}
/* reset the interrupt from the DSP */
iowrite32(C6205_HSR_INTSRC, phw->prHSR);
#endif
/* read the result */
if (time_out != 0)
*phr = interface->u.response_buffer;
/* set interface back to idle */
send_dsp_command(phw, H620_HIF_IDLE);
if ((time_out == 0) || (time_out2 == 0)) {
HPI_DEBUG_LOG(DEBUG, "something timed out!\n");
return hpi6205_error(0, HPI6205_ERROR_MSG_RESP_TIMEOUT);
}
/* special case for adapter close - */
/* wait for the DSP to indicate it is idle */
if (phm->function == HPI_ADAPTER_CLOSE) {
if (!wait_dsp_ack(phw, H620_HIF_IDLE, HPI6205_TIMEOUT)) {
HPI_DEBUG_LOG(DEBUG,
"timeout waiting for idle "
"(on adapter_close)\n");
return hpi6205_error(0,
HPI6205_ERROR_MSG_RESP_IDLE_TIMEOUT);
}
}
err = hpi_validate_response(phm, phr);
return err;
}
static void hw_message(struct hpi_adapter_obj *pao, struct hpi_message *phm,
struct hpi_response *phr)
{
u16 err = 0;
hpios_dsplock_lock(pao);
err = message_response_sequence(pao, phm, phr);
/* maybe an error response */
if (err) {
/* something failed in the HPI/DSP interface */
phr->error = err;
pao->dsp_crashed++;
/* just the header of the response is valid */
phr->size = sizeof(struct hpi_response_header);
goto err;
} else
pao->dsp_crashed = 0;
if (phr->error != 0) /* something failed in the DSP */
goto err;
switch (phm->function) {
case HPI_OSTREAM_WRITE:
case HPI_ISTREAM_ANC_WRITE:
err = hpi6205_transfer_data(pao, phm->u.d.u.data.pb_data,
phm->u.d.u.data.data_size, H620_HIF_SEND_DATA);
break;
case HPI_ISTREAM_READ:
case HPI_OSTREAM_ANC_READ:
err = hpi6205_transfer_data(pao, phm->u.d.u.data.pb_data,
phm->u.d.u.data.data_size, H620_HIF_GET_DATA);
break;
case HPI_CONTROL_SET_STATE:
if (phm->object == HPI_OBJ_CONTROLEX
&& phm->u.cx.attribute == HPI_COBRANET_SET_DATA)
err = hpi6205_transfer_data(pao,
phm->u.cx.u.cobranet_bigdata.pb_data,
phm->u.cx.u.cobranet_bigdata.byte_count,
H620_HIF_SEND_DATA);
break;
case HPI_CONTROL_GET_STATE:
if (phm->object == HPI_OBJ_CONTROLEX
&& phm->u.cx.attribute == HPI_COBRANET_GET_DATA)
err = hpi6205_transfer_data(pao,
phm->u.cx.u.cobranet_bigdata.pb_data,
phr->u.cx.u.cobranet_data.byte_count,
H620_HIF_GET_DATA);
break;
}
phr->error = err;
err:
hpios_dsplock_unlock(pao);
return;
}
/*****************************************************************************
AudioScience HPI driver
Copyright (C) 1997-2010 AudioScience Inc. <support@audioscience.com>
This program is free software; you can redistribute it and/or modify
it under the terms of version 2 of the GNU General Public License as
published by the Free Software Foundation;
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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
Host Interface module for an ASI6205 based
bus mastering PCI adapter.
Copyright AudioScience, Inc., 2003
******************************************************************************/
#ifndef _HPI6205_H_
#define _HPI6205_H_
/* transitional conditional compile shared between host and DSP */
/* #define HPI6205_NO_HSR_POLL */
#include "hpi_internal.h"
/***********************************************************
Defines used for basic messaging
************************************************************/
#define H620_HIF_RESET 0
#define H620_HIF_IDLE 1
#define H620_HIF_GET_RESP 2
#define H620_HIF_DATA_DONE 3
#define H620_HIF_DATA_MASK 0x10
#define H620_HIF_SEND_DATA 0x14
#define H620_HIF_GET_DATA 0x15
#define H620_HIF_UNKNOWN 0x0000ffff
/***********************************************************
Types used for mixer control caching
************************************************************/
#define H620_MAX_ISTREAMS 32
#define H620_MAX_OSTREAMS 32
#define HPI_NMIXER_CONTROLS 2048
/*********************************************************************
This is used for dynamic control cache allocation
**********************************************************************/
struct controlcache_6205 {
u32 number_of_controls;
u32 physical_address32;
u32 size_in_bytes;
};
/*********************************************************************
This is used for dynamic allocation of async event array
**********************************************************************/
struct async_event_buffer_6205 {
u32 physical_address32;
u32 spare;
struct hpi_fifo_buffer b;
};
/***********************************************************
The Host located memory buffer that the 6205 will bus master
in and out of.
************************************************************/
#define HPI6205_SIZEOF_DATA (16*1024)
struct bus_master_interface {
u32 host_cmd;
u32 dsp_ack;
u32 transfer_size_in_bytes;
union {
struct hpi_message message_buffer;
struct hpi_response response_buffer;
u8 b_data[HPI6205_SIZEOF_DATA];
} u;
struct controlcache_6205 control_cache;
struct async_event_buffer_6205 async_buffer;
struct hpi_hostbuffer_status
instream_host_buffer_status[H620_MAX_ISTREAMS];
struct hpi_hostbuffer_status
outstream_host_buffer_status[H620_MAX_OSTREAMS];
};
#endif
/******************************************************************************
AudioScience HPI driver
Copyright (C) 1997-2010 AudioScience Inc. <support@audioscience.com>
This program is free software; you can redistribute it and/or modify
it under the terms of version 2 of the GNU General Public License as
published by the Free Software Foundation;
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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
HPI internal definitions
(C) Copyright AudioScience Inc. 1996-2009
******************************************************************************/
#ifndef _HPI_INTERNAL_H_
#define _HPI_INTERNAL_H_
#include "hpi.h"
/** maximum number of memory regions mapped to an adapter */
#define HPI_MAX_ADAPTER_MEM_SPACES (2)
/* Each OS needs its own hpios.h, or specific define as above */
#include "hpios.h"
/* physical memory allocation */
void hpios_locked_mem_init(void
);
void hpios_locked_mem_free_all(void
);
#define hpios_locked_mem_prepare(a, b, c, d);
#define hpios_locked_mem_unprepare(a)
/** Allocate and map an area of locked memory for bus master DMA operations.
On success, *pLockedMemeHandle is a valid handle, and 0 is returned
On error *pLockedMemHandle marked invalid, non-zero returned.
If this function succeeds, then HpiOs_LockedMem_GetVirtAddr() and
HpiOs_LockedMem_GetPyhsAddr() will always succed on the returned handle.
*/
u16 hpios_locked_mem_alloc(struct consistent_dma_area *p_locked_mem_handle,
/**< memory handle */
u32 size, /**< size in bytes to allocate */
struct pci_dev *p_os_reference
/**< OS specific data required for memory allocation */
);
/** Free mapping and memory represented by LockedMemHandle
Frees any resources, then invalidates the handle.
Returns 0 on success, 1 if handle is invalid.
*/
u16 hpios_locked_mem_free(struct consistent_dma_area *locked_mem_handle);
/** Get the physical PCI address of memory represented by LockedMemHandle.
If handle is invalid *pPhysicalAddr is set to zero and return 1
*/
u16 hpios_locked_mem_get_phys_addr(struct consistent_dma_area
*locked_mem_handle, u32 *p_physical_addr);
/** Get the CPU address of of memory represented by LockedMemHandle.
If handle is NULL *ppvVirtualAddr is set to NULL and return 1
*/
u16 hpios_locked_mem_get_virt_addr(struct consistent_dma_area
*locked_mem_handle, void **ppv_virtual_addr);
/** Check that handle is valid
i.e it represents a valid memory area
*/
u16 hpios_locked_mem_valid(struct consistent_dma_area *locked_mem_handle);
/* timing/delay */
void hpios_delay_micro_seconds(u32 num_micro_sec);
struct hpi_message;
struct hpi_response;
typedef void hpi_handler_func(struct hpi_message *, struct hpi_response *);
/* If the assert fails, compiler complains
something like size of array `msg' is negative.
Unlike linux BUILD_BUG_ON, this works outside function scope.
*/
#define compile_time_assert(cond, msg) \
typedef char ASSERT_##msg[(cond) ? 1 : -1]
/*/////////////////////////////////////////////////////////////////////////// */
/* Private HPI Entity related definitions */
#define STR_SIZE_FIELD_MAX 65535U
#define STR_TYPE_FIELD_MAX 255U
#define STR_ROLE_FIELD_MAX 255U
struct hpi_entity_str {
uint16_t size;
uint8_t type;
uint8_t role;
};
#if defined(_MSC_VER)
#pragma warning(push)
#pragma warning(disable : 4200)
#endif
struct hpi_entity {
struct hpi_entity_str header;
#if ! defined(HPI_OS_DSP_C6000) || (defined(HPI_OS_DSP_C6000) && (__TI_COMPILER_VERSION__ > 6000008))
/* DSP C6000 compiler v6.0.8 and lower
do not support flexible array member */
uint8_t value[];
#else
/* NOTE! Using sizeof(struct hpi_entity) will give erroneous results */
#define HPI_INTERNAL_WARN_ABOUT_ENTITY_VALUE
uint8_t value[1];
#endif
};
#if defined(_MSC_VER)
#pragma warning(pop)
#endif
/******************************************* bus types */
enum HPI_BUSES {
HPI_BUS_ISAPNP = 1,
HPI_BUS_PCI = 2,
HPI_BUS_USB = 3,
HPI_BUS_NET = 4
};
/******************************************* CONTROL ATTRIBUTES ****/
/* (in order of control type ID */
/* This allows for 255 control types, 256 unique attributes each */
#define HPI_CTL_ATTR(ctl, ai) (HPI_CONTROL_##ctl * 0x100 + ai)
/* Get the sub-index of the attribute for a control type */
#define HPI_CTL_ATTR_INDEX(i) (i&0xff)
/* Generic control attributes. */
/** Enable a control.
0=disable, 1=enable
\note generic to all mixer plugins?
*/
#define HPI_GENERIC_ENABLE HPI_CTL_ATTR(GENERIC, 1)
/** Enable event generation for a control.
0=disable, 1=enable
\note generic to all controls that can generate events
*/
#define HPI_GENERIC_EVENT_ENABLE HPI_CTL_ATTR(GENERIC, 2)
/* Volume Control attributes */
#define HPI_VOLUME_GAIN HPI_CTL_ATTR(VOLUME, 1)
#define HPI_VOLUME_AUTOFADE HPI_CTL_ATTR(VOLUME, 2)
/** For HPI_ControlQuery() to get the number of channels of a volume control*/
#define HPI_VOLUME_NUM_CHANNELS HPI_CTL_ATTR(VOLUME, 6)
#define HPI_VOLUME_RANGE HPI_CTL_ATTR(VOLUME, 10)
/** Level Control attributes */
#define HPI_LEVEL_GAIN HPI_CTL_ATTR(LEVEL, 1)
#define HPI_LEVEL_RANGE HPI_CTL_ATTR(LEVEL, 10)
/* Meter Control attributes */
/** return RMS signal level */
#define HPI_METER_RMS HPI_CTL_ATTR(METER, 1)
/** return peak signal level */
#define HPI_METER_PEAK HPI_CTL_ATTR(METER, 2)
/** ballistics for ALL rms meters on adapter */
#define HPI_METER_RMS_BALLISTICS HPI_CTL_ATTR(METER, 3)
/** ballistics for ALL peak meters on adapter */
#define HPI_METER_PEAK_BALLISTICS HPI_CTL_ATTR(METER, 4)
/** For HPI_ControlQuery() to get the number of channels of a meter control*/
#define HPI_METER_NUM_CHANNELS HPI_CTL_ATTR(METER, 5)
/* Multiplexer control attributes */
#define HPI_MULTIPLEXER_SOURCE HPI_CTL_ATTR(MULTIPLEXER, 1)
#define HPI_MULTIPLEXER_QUERYSOURCE HPI_CTL_ATTR(MULTIPLEXER, 2)
/** AES/EBU transmitter control attributes */
/** AESEBU or SPDIF */
#define HPI_AESEBUTX_FORMAT HPI_CTL_ATTR(AESEBUTX, 1)
#define HPI_AESEBUTX_SAMPLERATE HPI_CTL_ATTR(AESEBUTX, 3)
#define HPI_AESEBUTX_CHANNELSTATUS HPI_CTL_ATTR(AESEBUTX, 4)
#define HPI_AESEBUTX_USERDATA HPI_CTL_ATTR(AESEBUTX, 5)
/** AES/EBU receiver control attributes */
#define HPI_AESEBURX_FORMAT HPI_CTL_ATTR(AESEBURX, 1)
#define HPI_AESEBURX_ERRORSTATUS HPI_CTL_ATTR(AESEBURX, 2)
#define HPI_AESEBURX_SAMPLERATE HPI_CTL_ATTR(AESEBURX, 3)
#define HPI_AESEBURX_CHANNELSTATUS HPI_CTL_ATTR(AESEBURX, 4)
#define HPI_AESEBURX_USERDATA HPI_CTL_ATTR(AESEBURX, 5)
/** \defgroup tuner_defs Tuners
\{
*/
/** \defgroup tuner_attrs Tuner control attributes
\{
*/
#define HPI_TUNER_BAND HPI_CTL_ATTR(TUNER, 1)
#define HPI_TUNER_FREQ HPI_CTL_ATTR(TUNER, 2)
#define HPI_TUNER_LEVEL HPI_CTL_ATTR(TUNER, 3)
#define HPI_TUNER_AUDIOMUTE HPI_CTL_ATTR(TUNER, 4)
/* use TUNER_STATUS instead */
#define HPI_TUNER_VIDEO_STATUS HPI_CTL_ATTR(TUNER, 5)
#define HPI_TUNER_GAIN HPI_CTL_ATTR(TUNER, 6)
#define HPI_TUNER_STATUS HPI_CTL_ATTR(TUNER, 7)
#define HPI_TUNER_MODE HPI_CTL_ATTR(TUNER, 8)
/** RDS data. */
#define HPI_TUNER_RDS HPI_CTL_ATTR(TUNER, 9)
/** Audio pre-emphasis. */
#define HPI_TUNER_DEEMPHASIS HPI_CTL_ATTR(TUNER, 10)
/** HD Radio tuner program control. */
#define HPI_TUNER_PROGRAM HPI_CTL_ATTR(TUNER, 11)
/** HD Radio tuner digital signal quality. */
#define HPI_TUNER_HDRADIO_SIGNAL_QUALITY HPI_CTL_ATTR(TUNER, 12)
/** HD Radio SDK firmware version. */
#define HPI_TUNER_HDRADIO_SDK_VERSION HPI_CTL_ATTR(TUNER, 13)
/** HD Radio DSP firmware version. */
#define HPI_TUNER_HDRADIO_DSP_VERSION HPI_CTL_ATTR(TUNER, 14)
/** \} */
/** \defgroup pads_attrs Tuner PADs control attributes
\{
*/
/** The text string containing the station/channel combination. */
#define HPI_PAD_CHANNEL_NAME HPI_CTL_ATTR(PAD, 1)
/** The text string containing the artist. */
#define HPI_PAD_ARTIST HPI_CTL_ATTR(PAD, 2)
/** The text string containing the title. */
#define HPI_PAD_TITLE HPI_CTL_ATTR(PAD, 3)
/** The text string containing the comment. */
#define HPI_PAD_COMMENT HPI_CTL_ATTR(PAD, 4)
/** The integer containing the PTY code. */
#define HPI_PAD_PROGRAM_TYPE HPI_CTL_ATTR(PAD, 5)
/** The integer containing the program identification. */
#define HPI_PAD_PROGRAM_ID HPI_CTL_ATTR(PAD, 6)
/** The integer containing whether traffic information is supported.
Contains either 1 or 0. */
#define HPI_PAD_TA_SUPPORT HPI_CTL_ATTR(PAD, 7)
/** The integer containing whether traffic announcement is in progress.
Contains either 1 or 0. */
#define HPI_PAD_TA_ACTIVE HPI_CTL_ATTR(PAD, 8)
/** \} */
/** \} */
/* VOX control attributes */
#define HPI_VOX_THRESHOLD HPI_CTL_ATTR(VOX, 1)
/*?? channel mode used hpi_multiplexer_source attribute == 1 */
#define HPI_CHANNEL_MODE_MODE HPI_CTL_ATTR(CHANNEL_MODE, 1)
/** \defgroup channel_modes Channel Modes
Used for HPI_ChannelModeSet/Get()
\{
*/
/** Left channel out = left channel in, Right channel out = right channel in. */
#define HPI_CHANNEL_MODE_NORMAL 1
/** Left channel out = right channel in, Right channel out = left channel in. */
#define HPI_CHANNEL_MODE_SWAP 2
/** Left channel out = left channel in, Right channel out = left channel in. */
#define HPI_CHANNEL_MODE_LEFT_TO_STEREO 3
/** Left channel out = right channel in, Right channel out = right channel in.*/
#define HPI_CHANNEL_MODE_RIGHT_TO_STEREO 4
/** Left channel out = (left channel in + right channel in)/2,
Right channel out = mute. */
#define HPI_CHANNEL_MODE_STEREO_TO_LEFT 5
/** Left channel out = mute,
Right channel out = (right channel in + left channel in)/2. */
#define HPI_CHANNEL_MODE_STEREO_TO_RIGHT 6
#define HPI_CHANNEL_MODE_LAST 6
/** \} */
/* Bitstream control set attributes */
#define HPI_BITSTREAM_DATA_POLARITY HPI_CTL_ATTR(BITSTREAM, 1)
#define HPI_BITSTREAM_CLOCK_EDGE HPI_CTL_ATTR(BITSTREAM, 2)
#define HPI_BITSTREAM_CLOCK_SOURCE HPI_CTL_ATTR(BITSTREAM, 3)
#define HPI_POLARITY_POSITIVE 0
#define HPI_POLARITY_NEGATIVE 1
/* Bitstream control get attributes */
#define HPI_BITSTREAM_ACTIVITY 1
/* SampleClock control attributes */
#define HPI_SAMPLECLOCK_SOURCE HPI_CTL_ATTR(SAMPLECLOCK, 1)
#define HPI_SAMPLECLOCK_SAMPLERATE HPI_CTL_ATTR(SAMPLECLOCK, 2)
#define HPI_SAMPLECLOCK_SOURCE_INDEX HPI_CTL_ATTR(SAMPLECLOCK, 3)
#define HPI_SAMPLECLOCK_LOCAL_SAMPLERATE\
HPI_CTL_ATTR(SAMPLECLOCK, 4)
#define HPI_SAMPLECLOCK_AUTO HPI_CTL_ATTR(SAMPLECLOCK, 5)
#define HPI_SAMPLECLOCK_LOCAL_LOCK HPI_CTL_ATTR(SAMPLECLOCK, 6)
/* Microphone control attributes */
#define HPI_MICROPHONE_PHANTOM_POWER HPI_CTL_ATTR(MICROPHONE, 1)
/** Equalizer control attributes
*/
/** Used to get number of filters in an EQ. (Can't set) */
#define HPI_EQUALIZER_NUM_FILTERS HPI_CTL_ATTR(EQUALIZER, 1)
/** Set/get the filter by type, freq, Q, gain */
#define HPI_EQUALIZER_FILTER HPI_CTL_ATTR(EQUALIZER, 2)
/** Get the biquad coefficients */
#define HPI_EQUALIZER_COEFFICIENTS HPI_CTL_ATTR(EQUALIZER, 3)
#define HPI_COMPANDER_PARAMS HPI_CTL_ATTR(COMPANDER, 1)
/* Cobranet control attributes.
MUST be distinct from all other control attributes.
This is so that host side processing can easily identify a Cobranet control
and apply additional host side operations (like copying data) as required.
*/
#define HPI_COBRANET_SET HPI_CTL_ATTR(COBRANET, 1)
#define HPI_COBRANET_GET HPI_CTL_ATTR(COBRANET, 2)
#define HPI_COBRANET_SET_DATA HPI_CTL_ATTR(COBRANET, 3)
#define HPI_COBRANET_GET_DATA HPI_CTL_ATTR(COBRANET, 4)
#define HPI_COBRANET_GET_STATUS HPI_CTL_ATTR(COBRANET, 5)
#define HPI_COBRANET_SEND_PACKET HPI_CTL_ATTR(COBRANET, 6)
#define HPI_COBRANET_GET_PACKET HPI_CTL_ATTR(COBRANET, 7)
/*------------------------------------------------------------
Cobranet Chip Bridge - copied from HMI.H
------------------------------------------------------------*/
#define HPI_COBRANET_HMI_cobra_bridge 0x20000
#define HPI_COBRANET_HMI_cobra_bridge_tx_pkt_buf \
(HPI_COBRANET_HMI_cobra_bridge + 0x1000)
#define HPI_COBRANET_HMI_cobra_bridge_rx_pkt_buf \
(HPI_COBRANET_HMI_cobra_bridge + 0x2000)
#define HPI_COBRANET_HMI_cobra_if_table1 0x110000
#define HPI_COBRANET_HMI_cobra_if_phy_address \
(HPI_COBRANET_HMI_cobra_if_table1 + 0xd)
#define HPI_COBRANET_HMI_cobra_protocolIP 0x72000
#define HPI_COBRANET_HMI_cobra_ip_mon_currentIP \
(HPI_COBRANET_HMI_cobra_protocolIP + 0x0)
#define HPI_COBRANET_HMI_cobra_ip_mon_staticIP \
(HPI_COBRANET_HMI_cobra_protocolIP + 0x2)
#define HPI_COBRANET_HMI_cobra_sys 0x100000
#define HPI_COBRANET_HMI_cobra_sys_desc \
(HPI_COBRANET_HMI_cobra_sys + 0x0)
#define HPI_COBRANET_HMI_cobra_sys_objectID \
(HPI_COBRANET_HMI_cobra_sys + 0x100)
#define HPI_COBRANET_HMI_cobra_sys_contact \
(HPI_COBRANET_HMI_cobra_sys + 0x200)
#define HPI_COBRANET_HMI_cobra_sys_name \
(HPI_COBRANET_HMI_cobra_sys + 0x300)
#define HPI_COBRANET_HMI_cobra_sys_location \
(HPI_COBRANET_HMI_cobra_sys + 0x400)
/*------------------------------------------------------------
Cobranet Chip Status bits
------------------------------------------------------------*/
#define HPI_COBRANET_HMI_STATUS_RXPACKET 2
#define HPI_COBRANET_HMI_STATUS_TXPACKET 3
/*------------------------------------------------------------
Ethernet header size
------------------------------------------------------------*/
#define HPI_ETHERNET_HEADER_SIZE (16)
/* These defines are used to fill in protocol information for an Ethernet packet
sent using HMI on CS18102 */
/** ID supplied by Cirrius for ASI packets. */
#define HPI_ETHERNET_PACKET_ID 0x85
/** Simple packet - no special routing required */
#define HPI_ETHERNET_PACKET_V1 0x01
/** This packet must make its way to the host across the HPI interface */
#define HPI_ETHERNET_PACKET_HOSTED_VIA_HMI 0x20
/** This packet must make its way to the host across the HPI interface */
#define HPI_ETHERNET_PACKET_HOSTED_VIA_HMI_V1 0x21
/** This packet must make its way to the host across the HPI interface */
#define HPI_ETHERNET_PACKET_HOSTED_VIA_HPI 0x40
/** This packet must make its way to the host across the HPI interface */
#define HPI_ETHERNET_PACKET_HOSTED_VIA_HPI_V1 0x41
#define HPI_ETHERNET_UDP_PORT (44600) /*!< UDP messaging port */
/** Base network time out is set to 100 milli-seconds. */
#define HPI_ETHERNET_TIMEOUT_MS (100)
/** \defgroup tonedet_attr Tonedetector attributes
\{
Used by HPI_ToneDetector_Set() and HPI_ToneDetector_Get()
*/
/** Set the threshold level of a tonedetector,
Threshold is a -ve number in units of dB/100,
*/
#define HPI_TONEDETECTOR_THRESHOLD HPI_CTL_ATTR(TONEDETECTOR, 1)
/** Get the current state of tonedetection
The result is a bitmap of detected tones. pairs of bits represent the left
and right channels, with left channel in LSB.
The lowest frequency detector state is in the LSB
*/
#define HPI_TONEDETECTOR_STATE HPI_CTL_ATTR(TONEDETECTOR, 2)
/** Get the frequency of a tonedetector band.
*/
#define HPI_TONEDETECTOR_FREQUENCY HPI_CTL_ATTR(TONEDETECTOR, 3)
/**\}*/
/** \defgroup silencedet_attr SilenceDetector attributes
\{
*/
/** Get the current state of tonedetection
The result is a bitmap with 1s for silent channels. Left channel is in LSB
*/
#define HPI_SILENCEDETECTOR_STATE \
HPI_CTL_ATTR(SILENCEDETECTOR, 2)
/** Set the threshold level of a SilenceDetector,
Threshold is a -ve number in units of dB/100,
*/
#define HPI_SILENCEDETECTOR_THRESHOLD \
HPI_CTL_ATTR(SILENCEDETECTOR, 1)
/** get/set the silence time before the detector triggers
*/
#define HPI_SILENCEDETECTOR_DELAY \
HPI_CTL_ATTR(SILENCEDETECTOR, 3)
/**\}*/
/* Locked memory buffer alloc/free phases */
/** use one message to allocate or free physical memory */
#define HPI_BUFFER_CMD_EXTERNAL 0
/** alloc physical memory */
#define HPI_BUFFER_CMD_INTERNAL_ALLOC 1
/** send physical memory address to adapter */
#define HPI_BUFFER_CMD_INTERNAL_GRANTADAPTER 2
/** notify adapter to stop using physical buffer */
#define HPI_BUFFER_CMD_INTERNAL_REVOKEADAPTER 3
/** free physical buffer */
#define HPI_BUFFER_CMD_INTERNAL_FREE 4
/******************************************* CONTROLX ATTRIBUTES ****/
/* NOTE: All controlx attributes must be unique, unlike control attributes */
/*****************************************************************************/
/*****************************************************************************/
/******** HPI LOW LEVEL MESSAGES *******/
/*****************************************************************************/
/*****************************************************************************/
/** Pnp ids */
/** "ASI" - actual is "ASX" - need to change */
#define HPI_ID_ISAPNP_AUDIOSCIENCE 0x0669
/** PCI vendor ID that AudioScience uses */
#define HPI_PCI_VENDOR_ID_AUDIOSCIENCE 0x175C
/** PCI vendor ID that the DSP56301 has */
#define HPI_PCI_VENDOR_ID_MOTOROLA 0x1057
/** PCI vendor ID that TI uses */
#define HPI_PCI_VENDOR_ID_TI 0x104C
#define HPI_PCI_DEV_ID_PCI2040 0xAC60
/** TI's C6205 PCI interface has this ID */
#define HPI_PCI_DEV_ID_DSP6205 0xA106
#define HPI_USB_VENDOR_ID_AUDIOSCIENCE 0x1257
#define HPI_USB_W2K_TAG 0x57495341 /* "ASIW" */
#define HPI_USB_LINUX_TAG 0x4C495341 /* "ASIL" */
/** First 2 hex digits define the adapter family */
#define HPI_ADAPTER_FAMILY_MASK 0xff00
#define HPI_ADAPTER_FAMILY_ASI(f) (f & HPI_ADAPTER_FAMILY_MASK)
#define HPI_ADAPTER_ASI(f) (f)
/******************************************* message types */
#define HPI_TYPE_MESSAGE 1
#define HPI_TYPE_RESPONSE 2
#define HPI_TYPE_DATA 3
#define HPI_TYPE_SSX2BYPASS_MESSAGE 4
/******************************************* object types */
#define HPI_OBJ_SUBSYSTEM 1
#define HPI_OBJ_ADAPTER 2
#define HPI_OBJ_OSTREAM 3
#define HPI_OBJ_ISTREAM 4
#define HPI_OBJ_MIXER 5
#define HPI_OBJ_NODE 6
#define HPI_OBJ_CONTROL 7
#define HPI_OBJ_NVMEMORY 8
#define HPI_OBJ_GPIO 9
#define HPI_OBJ_WATCHDOG 10
#define HPI_OBJ_CLOCK 11
#define HPI_OBJ_PROFILE 12
#define HPI_OBJ_CONTROLEX 13
#define HPI_OBJ_ASYNCEVENT 14
#define HPI_OBJ_MAXINDEX 14
/******************************************* methods/functions */
#define HPI_OBJ_FUNCTION_SPACING 0x100
#define HPI_MAKE_INDEX(obj, index) (obj * HPI_OBJ_FUNCTION_SPACING + index)
#define HPI_EXTRACT_INDEX(fn) (fn & 0xff)
/* SUB-SYSTEM */
#define HPI_SUBSYS_OPEN HPI_MAKE_INDEX(HPI_OBJ_SUBSYSTEM, 1)
#define HPI_SUBSYS_GET_VERSION HPI_MAKE_INDEX(HPI_OBJ_SUBSYSTEM, 2)
#define HPI_SUBSYS_GET_INFO HPI_MAKE_INDEX(HPI_OBJ_SUBSYSTEM, 3)
#define HPI_SUBSYS_FIND_ADAPTERS HPI_MAKE_INDEX(HPI_OBJ_SUBSYSTEM, 4)
#define HPI_SUBSYS_CREATE_ADAPTER HPI_MAKE_INDEX(HPI_OBJ_SUBSYSTEM, 5)
#define HPI_SUBSYS_CLOSE HPI_MAKE_INDEX(HPI_OBJ_SUBSYSTEM, 6)
#define HPI_SUBSYS_DELETE_ADAPTER HPI_MAKE_INDEX(HPI_OBJ_SUBSYSTEM, 7)
#define HPI_SUBSYS_DRIVER_LOAD HPI_MAKE_INDEX(HPI_OBJ_SUBSYSTEM, 8)
#define HPI_SUBSYS_DRIVER_UNLOAD HPI_MAKE_INDEX(HPI_OBJ_SUBSYSTEM, 9)
#define HPI_SUBSYS_READ_PORT_8 HPI_MAKE_INDEX(HPI_OBJ_SUBSYSTEM, 10)
#define HPI_SUBSYS_WRITE_PORT_8 HPI_MAKE_INDEX(HPI_OBJ_SUBSYSTEM, 11)
#define HPI_SUBSYS_GET_NUM_ADAPTERS HPI_MAKE_INDEX(HPI_OBJ_SUBSYSTEM, 12)
#define HPI_SUBSYS_GET_ADAPTER HPI_MAKE_INDEX(HPI_OBJ_SUBSYSTEM, 13)
#define HPI_SUBSYS_SET_NETWORK_INTERFACE HPI_MAKE_INDEX(HPI_OBJ_SUBSYSTEM, 14)
#define HPI_SUBSYS_FUNCTION_COUNT 14
/* ADAPTER */
#define HPI_ADAPTER_OPEN HPI_MAKE_INDEX(HPI_OBJ_ADAPTER, 1)
#define HPI_ADAPTER_CLOSE HPI_MAKE_INDEX(HPI_OBJ_ADAPTER, 2)
#define HPI_ADAPTER_GET_INFO HPI_MAKE_INDEX(HPI_OBJ_ADAPTER, 3)
#define HPI_ADAPTER_GET_ASSERT HPI_MAKE_INDEX(HPI_OBJ_ADAPTER, 4)
#define HPI_ADAPTER_TEST_ASSERT HPI_MAKE_INDEX(HPI_OBJ_ADAPTER, 5)
#define HPI_ADAPTER_SET_MODE HPI_MAKE_INDEX(HPI_OBJ_ADAPTER, 6)
#define HPI_ADAPTER_GET_MODE HPI_MAKE_INDEX(HPI_OBJ_ADAPTER, 7)
#define HPI_ADAPTER_ENABLE_CAPABILITY HPI_MAKE_INDEX(HPI_OBJ_ADAPTER, 8)
#define HPI_ADAPTER_SELFTEST HPI_MAKE_INDEX(HPI_OBJ_ADAPTER, 9)
#define HPI_ADAPTER_FIND_OBJECT HPI_MAKE_INDEX(HPI_OBJ_ADAPTER, 10)
#define HPI_ADAPTER_QUERY_FLASH HPI_MAKE_INDEX(HPI_OBJ_ADAPTER, 11)
#define HPI_ADAPTER_START_FLASH HPI_MAKE_INDEX(HPI_OBJ_ADAPTER, 12)
#define HPI_ADAPTER_PROGRAM_FLASH HPI_MAKE_INDEX(HPI_OBJ_ADAPTER, 13)
#define HPI_ADAPTER_SET_PROPERTY HPI_MAKE_INDEX(HPI_OBJ_ADAPTER, 14)
#define HPI_ADAPTER_GET_PROPERTY HPI_MAKE_INDEX(HPI_OBJ_ADAPTER, 15)
#define HPI_ADAPTER_ENUM_PROPERTY HPI_MAKE_INDEX(HPI_OBJ_ADAPTER, 16)
#define HPI_ADAPTER_MODULE_INFO HPI_MAKE_INDEX(HPI_OBJ_ADAPTER, 17)
#define HPI_ADAPTER_DEBUG_READ HPI_MAKE_INDEX(HPI_OBJ_ADAPTER, 18)
#define HPI_ADAPTER_FUNCTION_COUNT 18
/* OUTPUT STREAM */
#define HPI_OSTREAM_OPEN HPI_MAKE_INDEX(HPI_OBJ_OSTREAM, 1)
#define HPI_OSTREAM_CLOSE HPI_MAKE_INDEX(HPI_OBJ_OSTREAM, 2)
#define HPI_OSTREAM_WRITE HPI_MAKE_INDEX(HPI_OBJ_OSTREAM, 3)
#define HPI_OSTREAM_START HPI_MAKE_INDEX(HPI_OBJ_OSTREAM, 4)
#define HPI_OSTREAM_STOP HPI_MAKE_INDEX(HPI_OBJ_OSTREAM, 5)
#define HPI_OSTREAM_RESET HPI_MAKE_INDEX(HPI_OBJ_OSTREAM, 6)
#define HPI_OSTREAM_GET_INFO HPI_MAKE_INDEX(HPI_OBJ_OSTREAM, 7)
#define HPI_OSTREAM_QUERY_FORMAT HPI_MAKE_INDEX(HPI_OBJ_OSTREAM, 8)
#define HPI_OSTREAM_DATA HPI_MAKE_INDEX(HPI_OBJ_OSTREAM, 9)
#define HPI_OSTREAM_SET_VELOCITY HPI_MAKE_INDEX(HPI_OBJ_OSTREAM, 10)
#define HPI_OSTREAM_SET_PUNCHINOUT HPI_MAKE_INDEX(HPI_OBJ_OSTREAM, 11)
#define HPI_OSTREAM_SINEGEN HPI_MAKE_INDEX(HPI_OBJ_OSTREAM, 12)
#define HPI_OSTREAM_ANC_RESET HPI_MAKE_INDEX(HPI_OBJ_OSTREAM, 13)
#define HPI_OSTREAM_ANC_GET_INFO HPI_MAKE_INDEX(HPI_OBJ_OSTREAM, 14)
#define HPI_OSTREAM_ANC_READ HPI_MAKE_INDEX(HPI_OBJ_OSTREAM, 15)
#define HPI_OSTREAM_SET_TIMESCALE HPI_MAKE_INDEX(HPI_OBJ_OSTREAM, 16)
#define HPI_OSTREAM_SET_FORMAT HPI_MAKE_INDEX(HPI_OBJ_OSTREAM, 17)
#define HPI_OSTREAM_HOSTBUFFER_ALLOC HPI_MAKE_INDEX(HPI_OBJ_OSTREAM, 18)
#define HPI_OSTREAM_HOSTBUFFER_FREE HPI_MAKE_INDEX(HPI_OBJ_OSTREAM, 19)
#define HPI_OSTREAM_GROUP_ADD HPI_MAKE_INDEX(HPI_OBJ_OSTREAM, 20)
#define HPI_OSTREAM_GROUP_GETMAP HPI_MAKE_INDEX(HPI_OBJ_OSTREAM, 21)
#define HPI_OSTREAM_GROUP_RESET HPI_MAKE_INDEX(HPI_OBJ_OSTREAM, 22)
#define HPI_OSTREAM_HOSTBUFFER_GET_INFO HPI_MAKE_INDEX(HPI_OBJ_OSTREAM, 23)
#define HPI_OSTREAM_WAIT_START HPI_MAKE_INDEX(HPI_OBJ_OSTREAM, 24)
#define HPI_OSTREAM_FUNCTION_COUNT 24
/* INPUT STREAM */
#define HPI_ISTREAM_OPEN HPI_MAKE_INDEX(HPI_OBJ_ISTREAM, 1)
#define HPI_ISTREAM_CLOSE HPI_MAKE_INDEX(HPI_OBJ_ISTREAM, 2)
#define HPI_ISTREAM_SET_FORMAT HPI_MAKE_INDEX(HPI_OBJ_ISTREAM, 3)
#define HPI_ISTREAM_READ HPI_MAKE_INDEX(HPI_OBJ_ISTREAM, 4)
#define HPI_ISTREAM_START HPI_MAKE_INDEX(HPI_OBJ_ISTREAM, 5)
#define HPI_ISTREAM_STOP HPI_MAKE_INDEX(HPI_OBJ_ISTREAM, 6)
#define HPI_ISTREAM_RESET HPI_MAKE_INDEX(HPI_OBJ_ISTREAM, 7)
#define HPI_ISTREAM_GET_INFO HPI_MAKE_INDEX(HPI_OBJ_ISTREAM, 8)
#define HPI_ISTREAM_QUERY_FORMAT HPI_MAKE_INDEX(HPI_OBJ_ISTREAM, 9)
#define HPI_ISTREAM_ANC_RESET HPI_MAKE_INDEX(HPI_OBJ_ISTREAM, 10)
#define HPI_ISTREAM_ANC_GET_INFO HPI_MAKE_INDEX(HPI_OBJ_ISTREAM, 11)
#define HPI_ISTREAM_ANC_WRITE HPI_MAKE_INDEX(HPI_OBJ_ISTREAM, 12)
#define HPI_ISTREAM_HOSTBUFFER_ALLOC HPI_MAKE_INDEX(HPI_OBJ_ISTREAM, 13)
#define HPI_ISTREAM_HOSTBUFFER_FREE HPI_MAKE_INDEX(HPI_OBJ_ISTREAM, 14)
#define HPI_ISTREAM_GROUP_ADD HPI_MAKE_INDEX(HPI_OBJ_ISTREAM, 15)
#define HPI_ISTREAM_GROUP_GETMAP HPI_MAKE_INDEX(HPI_OBJ_ISTREAM, 16)
#define HPI_ISTREAM_GROUP_RESET HPI_MAKE_INDEX(HPI_OBJ_ISTREAM, 17)
#define HPI_ISTREAM_HOSTBUFFER_GET_INFO HPI_MAKE_INDEX(HPI_OBJ_ISTREAM, 18)
#define HPI_ISTREAM_WAIT_START HPI_MAKE_INDEX(HPI_OBJ_ISTREAM, 19)
#define HPI_ISTREAM_FUNCTION_COUNT 19
/* MIXER */
/* NOTE:
GET_NODE_INFO, SET_CONNECTION, GET_CONNECTIONS are not currently used */
#define HPI_MIXER_OPEN HPI_MAKE_INDEX(HPI_OBJ_MIXER, 1)
#define HPI_MIXER_CLOSE HPI_MAKE_INDEX(HPI_OBJ_MIXER, 2)
#define HPI_MIXER_GET_INFO HPI_MAKE_INDEX(HPI_OBJ_MIXER, 3)
#define HPI_MIXER_GET_NODE_INFO HPI_MAKE_INDEX(HPI_OBJ_MIXER, 4)
#define HPI_MIXER_GET_CONTROL HPI_MAKE_INDEX(HPI_OBJ_MIXER, 5)
#define HPI_MIXER_SET_CONNECTION HPI_MAKE_INDEX(HPI_OBJ_MIXER, 6)
#define HPI_MIXER_GET_CONNECTIONS HPI_MAKE_INDEX(HPI_OBJ_MIXER, 7)
#define HPI_MIXER_GET_CONTROL_BY_INDEX HPI_MAKE_INDEX(HPI_OBJ_MIXER, 8)
#define HPI_MIXER_GET_CONTROL_ARRAY_BY_INDEX HPI_MAKE_INDEX(HPI_OBJ_MIXER, 9)
#define HPI_MIXER_GET_CONTROL_MULTIPLE_VALUES HPI_MAKE_INDEX(HPI_OBJ_MIXER, 10)
#define HPI_MIXER_STORE HPI_MAKE_INDEX(HPI_OBJ_MIXER, 11)
#define HPI_MIXER_FUNCTION_COUNT 11
/* MIXER CONTROLS */
#define HPI_CONTROL_GET_INFO HPI_MAKE_INDEX(HPI_OBJ_CONTROL, 1)
#define HPI_CONTROL_GET_STATE HPI_MAKE_INDEX(HPI_OBJ_CONTROL, 2)
#define HPI_CONTROL_SET_STATE HPI_MAKE_INDEX(HPI_OBJ_CONTROL, 3)
#define HPI_CONTROL_FUNCTION_COUNT 3
/* NONVOL MEMORY */
#define HPI_NVMEMORY_OPEN HPI_MAKE_INDEX(HPI_OBJ_NVMEMORY, 1)
#define HPI_NVMEMORY_READ_BYTE HPI_MAKE_INDEX(HPI_OBJ_NVMEMORY, 2)
#define HPI_NVMEMORY_WRITE_BYTE HPI_MAKE_INDEX(HPI_OBJ_NVMEMORY, 3)
#define HPI_NVMEMORY_FUNCTION_COUNT 3
/* GPIO */
#define HPI_GPIO_OPEN HPI_MAKE_INDEX(HPI_OBJ_GPIO, 1)
#define HPI_GPIO_READ_BIT HPI_MAKE_INDEX(HPI_OBJ_GPIO, 2)
#define HPI_GPIO_WRITE_BIT HPI_MAKE_INDEX(HPI_OBJ_GPIO, 3)
#define HPI_GPIO_READ_ALL HPI_MAKE_INDEX(HPI_OBJ_GPIO, 4)
#define HPI_GPIO_WRITE_STATUS HPI_MAKE_INDEX(HPI_OBJ_GPIO, 5)
#define HPI_GPIO_FUNCTION_COUNT 5
/* ASYNC EVENT */
#define HPI_ASYNCEVENT_OPEN HPI_MAKE_INDEX(HPI_OBJ_ASYNCEVENT, 1)
#define HPI_ASYNCEVENT_CLOSE HPI_MAKE_INDEX(HPI_OBJ_ASYNCEVENT, 2)
#define HPI_ASYNCEVENT_WAIT HPI_MAKE_INDEX(HPI_OBJ_ASYNCEVENT, 3)
#define HPI_ASYNCEVENT_GETCOUNT HPI_MAKE_INDEX(HPI_OBJ_ASYNCEVENT, 4)
#define HPI_ASYNCEVENT_GET HPI_MAKE_INDEX(HPI_OBJ_ASYNCEVENT, 5)
#define HPI_ASYNCEVENT_SENDEVENTS HPI_MAKE_INDEX(HPI_OBJ_ASYNCEVENT, 6)
#define HPI_ASYNCEVENT_FUNCTION_COUNT 6
/* WATCH-DOG */
#define HPI_WATCHDOG_OPEN HPI_MAKE_INDEX(HPI_OBJ_WATCHDOG, 1)
#define HPI_WATCHDOG_SET_TIME HPI_MAKE_INDEX(HPI_OBJ_WATCHDOG, 2)
#define HPI_WATCHDOG_PING HPI_MAKE_INDEX(HPI_OBJ_WATCHDOG, 3)
/* CLOCK */
#define HPI_CLOCK_OPEN HPI_MAKE_INDEX(HPI_OBJ_CLOCK, 1)
#define HPI_CLOCK_SET_TIME HPI_MAKE_INDEX(HPI_OBJ_CLOCK, 2)
#define HPI_CLOCK_GET_TIME HPI_MAKE_INDEX(HPI_OBJ_CLOCK, 3)
/* PROFILE */
#define HPI_PROFILE_OPEN_ALL HPI_MAKE_INDEX(HPI_OBJ_PROFILE, 1)
#define HPI_PROFILE_START_ALL HPI_MAKE_INDEX(HPI_OBJ_PROFILE, 2)
#define HPI_PROFILE_STOP_ALL HPI_MAKE_INDEX(HPI_OBJ_PROFILE, 3)
#define HPI_PROFILE_GET HPI_MAKE_INDEX(HPI_OBJ_PROFILE, 4)
#define HPI_PROFILE_GET_IDLECOUNT HPI_MAKE_INDEX(HPI_OBJ_PROFILE, 5)
#define HPI_PROFILE_GET_NAME HPI_MAKE_INDEX(HPI_OBJ_PROFILE, 6)
#define HPI_PROFILE_GET_UTILIZATION HPI_MAKE_INDEX(HPI_OBJ_PROFILE, 7)
#define HPI_PROFILE_FUNCTION_COUNT 7
/* ////////////////////////////////////////////////////////////////////// */
/* PRIVATE ATTRIBUTES */
/* ////////////////////////////////////////////////////////////////////// */
/* STRUCTURES */
#ifndef DISABLE_PRAGMA_PACK1
#pragma pack(push, 1)
#endif
/** PCI bus resource */
struct hpi_pci {
u32 __iomem *ap_mem_base[HPI_MAX_ADAPTER_MEM_SPACES];
struct pci_dev *p_os_data;
#ifndef HPI64BIT /* keep structure size constant */
u32 padding[HPI_MAX_ADAPTER_MEM_SPACES + 1];
#endif
u16 vendor_id;
u16 device_id;
u16 subsys_vendor_id;
u16 subsys_device_id;
u16 bus_number;
u16 device_number;
u32 interrupt;
};
struct hpi_resource {
union {
const struct hpi_pci *pci;
const char *net_if;
} r;
#ifndef HPI64BIT /* keep structure size constant */
u32 pad_to64;
#endif
u16 bus_type; /* HPI_BUS_PNPISA, _PCI, _USB etc */
u16 padding;
};
/** Format info used inside struct hpi_message
Not the same as public API struct hpi_format */
struct hpi_msg_format {
u32 sample_rate;
/**< 11025, 32000, 44100 ... */
u32 bit_rate; /**< for MPEG */
u32 attributes;
/**< Stereo/JointStereo/Mono */
u16 channels; /**< 1,2..., (or ancillary mode or idle bit */
u16 format; /**< HPI_FORMAT_PCM16, _MPEG etc. see \ref HPI_FORMATS. */
};
/** Buffer+format structure.
Must be kept 7 * 32 bits to match public struct hpi_datastruct */
struct hpi_msg_data {
struct hpi_msg_format format;
u8 *pb_data;
#ifndef HPI64BIT
u32 padding;
#endif
u32 data_size;
};
/** struct hpi_datastructure used up to 3.04 driver */
struct hpi_data_legacy32 {
struct hpi_format format;
u32 pb_data;
u32 data_size;
};
#ifdef HPI64BIT
/* Compatibility version of struct hpi_data*/
struct hpi_data_compat32 {
struct hpi_msg_format format;
u32 pb_data;
u32 padding;
u32 data_size;
};
#endif
struct hpi_buffer {
/** placehoder for backward compatability (see dwBufferSize) */
struct hpi_msg_format reserved;
u32 command; /**< HPI_BUFFER_CMD_xxx*/
u32 pci_address; /**< PCI physical address of buffer for DSP DMA */
u32 buffer_size; /**< must line up with data_size of HPI_DATA*/
};
/*/////////////////////////////////////////////////////////////////////////// */
/* This is used for background buffer bus mastering stream buffers. */
struct hpi_hostbuffer_status {
u32 samples_processed;
u32 auxiliary_data_available;
u32 stream_state;
/* DSP index in to the host bus master buffer. */
u32 dSP_index;
/* Host index in to the host bus master buffer. */
u32 host_index;
u32 size_in_bytes;
};
struct hpi_streamid {
u16 object_type;
/**< Type of object, HPI_OBJ_OSTREAM or HPI_OBJ_ISTREAM. */
u16 stream_index; /**< outstream or instream index. */
};
struct hpi_punchinout {
u32 punch_in_sample;
u32 punch_out_sample;
};
struct hpi_subsys_msg {
struct hpi_resource resource;
};
struct hpi_subsys_res {
u32 version;
u32 data; /* used to return extended version */
u16 num_adapters; /* number of adapters */
u16 adapter_index;
u16 aw_adapter_list[HPI_MAX_ADAPTERS];
};
struct hpi_adapter_msg {
u32 adapter_mode; /* adapter mode */
u16 assert_id; /* assert number for "test assert" call
object_index for find object call
query_or_set for hpi_adapter_set_mode_ex() */
u16 object_type; /* for adapter find object call */
};
union hpi_adapterx_msg {
struct hpi_adapter_msg adapter;
struct {
u32 offset;
} query_flash;
struct {
u32 offset;
u32 length;
u32 key;
} start_flash;
struct {
u32 checksum;
u16 sequence;
u16 length;
u16 offset; /**< offset from start of msg to data */
u16 unused;
} program_flash;
struct {
u16 property;
u16 parameter1;
u16 parameter2;
} property_set;
struct {
u16 index;
u16 what;
u16 property_index;
} property_enum;
struct {
u16 index;
} module_info;
struct {
u32 dsp_address;
u32 count_bytes;
} debug_read;
};
struct hpi_adapter_res {
u32 serial_number;
u16 adapter_type;
u16 adapter_index; /* is this needed? also used for dsp_index */
u16 num_instreams;
u16 num_outstreams;
u16 num_mixers;
u16 version;
u8 sz_adapter_assert[HPI_STRING_LEN];
};
union hpi_adapterx_res {
struct hpi_adapter_res adapter;
struct {
u32 checksum;
u32 length;
u32 version;
} query_flash;
struct {
u16 sequence;
} program_flash;
struct {
u16 parameter1;
u16 parameter2;
} property_get;
};
struct hpi_stream_msg {
union {
struct hpi_msg_data data;
struct hpi_data_legacy32 data32;
u16 velocity;
struct hpi_punchinout pio;
u32 time_scale;
struct hpi_buffer buffer;
struct hpi_streamid stream;
} u;
};
struct hpi_stream_res {
union {
struct {
/* size of hardware buffer */
u32 buffer_size;
/* OutStream - data to play,
InStream - data recorded */
u32 data_available;
/* OutStream - samples played,
InStream - samples recorded */
u32 samples_transferred;
/* Adapter - OutStream - data to play,
InStream - data recorded */
u32 auxiliary_data_available;
u16 state; /* HPI_STATE_PLAYING, _STATE_STOPPED */
u16 padding;
} stream_info;
struct {
u32 buffer_size;
u32 data_available;
u32 samples_transfered;
u16 state;
u16 outstream_index;
u16 instream_index;
u16 padding;
u32 auxiliary_data_available;
} legacy_stream_info;
struct {
/* bitmap of grouped OutStreams */
u32 outstream_group_map;
/* bitmap of grouped InStreams */
u32 instream_group_map;
} group_info;
struct {
/* pointer to the buffer */
u8 *p_buffer;
/* pointer to the hostbuffer status */
struct hpi_hostbuffer_status *p_status;
} hostbuffer_info;
} u;
};
struct hpi_mixer_msg {
u16 control_index;
u16 control_type; /* = HPI_CONTROL_METER _VOLUME etc */
u16 padding1; /* maintain alignment of subsequent fields */
u16 node_type1; /* = HPI_SOURCENODE_LINEIN etc */
u16 node_index1; /* = 0..N */
u16 node_type2;
u16 node_index2;
u16 padding2; /* round to 4 bytes */
};
struct hpi_mixer_res {
u16 src_node_type; /* = HPI_SOURCENODE_LINEIN etc */
u16 src_node_index; /* = 0..N */
u16 dst_node_type;
u16 dst_node_index;
/* Also controlType for MixerGetControlByIndex */
u16 control_index;
/* may indicate which DSP the control is located on */
u16 dsp_index;
};
union hpi_mixerx_msg {
struct {
u16 starting_index;
u16 flags;
u32 length_in_bytes; /* length in bytes of p_data */
u32 p_data; /* pointer to a data array */
} gcabi;
struct {
u16 command;
u16 index;
} store; /* for HPI_MIXER_STORE message */
};
union hpi_mixerx_res {
struct {
u32 bytes_returned; /* size of items returned */
u32 p_data; /* pointer to data array */
u16 more_to_do; /* indicates if there is more to do */
} gcabi;
};
struct hpi_control_msg {
u16 attribute; /* control attribute or property */
u16 saved_index;
u32 param1; /* generic parameter 1 */
u32 param2; /* generic parameter 2 */
short an_log_value[HPI_MAX_CHANNELS];
};
struct hpi_control_union_msg {
u16 attribute; /* control attribute or property */
u16 saved_index; /* only used in ctrl save/restore */
union {
struct {
u32 param1; /* generic parameter 1 */
u32 param2; /* generic parameter 2 */
short an_log_value[HPI_MAX_CHANNELS];
} old;
union {
u32 frequency;
u32 gain;
u32 band;
u32 deemphasis;
u32 program;
struct {
u32 mode;
u32 value;
} mode;
} tuner;
} u;
};
struct hpi_control_res {
/* Could make union. dwParam, anLogValue never used in same response */
u32 param1;
u32 param2;
short an_log_value[HPI_MAX_CHANNELS];
};
union hpi_control_union_res {
struct {
u32 param1;
u32 param2;
short an_log_value[HPI_MAX_CHANNELS];
} old;
union {
u32 band;
u32 frequency;
u32 gain;
u32 level;
u32 deemphasis;
struct {
u32 data[2];
u32 bLER;
} rds;
} tuner;
struct {
char sz_data[8];
u32 remaining_chars;
} chars8;
char c_data12[12];
};
/* HPI_CONTROLX_STRUCTURES */
/* Message */
/** Used for all HMI variables where max length <= 8 bytes
*/
struct hpi_controlx_msg_cobranet_data {
u32 hmi_address;
u32 byte_count;
u32 data[2];
};
/** Used for string data, and for packet bridge
*/
struct hpi_controlx_msg_cobranet_bigdata {
u32 hmi_address;
u32 byte_count;
u8 *pb_data;
#ifndef HPI64BIT
u32 padding;
#endif
};
/** Used for PADS control reading of string fields.
*/
struct hpi_controlx_msg_pad_data {
u32 field;
u32 byte_count;
u8 *pb_data;
#ifndef HPI64BIT
u32 padding;
#endif
};
/** Used for generic data
*/
struct hpi_controlx_msg_generic {
u32 param1;
u32 param2;
};
struct hpi_controlx_msg {
u16 attribute; /* control attribute or property */
u16 saved_index;
union {
struct hpi_controlx_msg_cobranet_data cobranet_data;
struct hpi_controlx_msg_cobranet_bigdata cobranet_bigdata;
struct hpi_controlx_msg_generic generic;
struct hpi_controlx_msg_pad_data pad_data;
/*struct param_value universal_value; */
/* nothing extra to send for status read */
} u;
};
/* Response */
/**
*/
struct hpi_controlx_res_cobranet_data {
u32 byte_count;
u32 data[2];
};
struct hpi_controlx_res_cobranet_bigdata {
u32 byte_count;
};
struct hpi_controlx_res_cobranet_status {
u32 status;
u32 readable_size;
u32 writeable_size;
};
struct hpi_controlx_res_generic {
u32 param1;
u32 param2;
};
struct hpi_controlx_res {
union {
struct hpi_controlx_res_cobranet_bigdata cobranet_bigdata;
struct hpi_controlx_res_cobranet_data cobranet_data;
struct hpi_controlx_res_cobranet_status cobranet_status;
struct hpi_controlx_res_generic generic;
/*struct param_info universal_info; */
/*struct param_value universal_value; */
} u;
};
struct hpi_nvmemory_msg {
u16 address;
u16 data;
};
struct hpi_nvmemory_res {
u16 size_in_bytes;
u16 data;
};
struct hpi_gpio_msg {
u16 bit_index;
u16 bit_data;
};
struct hpi_gpio_res {
u16 number_input_bits;
u16 number_output_bits;
u16 bit_data[4];
};
struct hpi_async_msg {
u32 events;
u16 maximum_events;
u16 padding;
};
struct hpi_async_res {
union {
struct {
u16 count;
} count;
struct {
u32 events;
u16 number_returned;
u16 padding;
} get;
struct hpi_async_event event;
} u;
};
struct hpi_watchdog_msg {
u32 time_ms;
};
struct hpi_watchdog_res {
u32 time_ms;
};
struct hpi_clock_msg {
u16 hours;
u16 minutes;
u16 seconds;
u16 milli_seconds;
};
struct hpi_clock_res {
u16 size_in_bytes;
u16 hours;
u16 minutes;
u16 seconds;
u16 milli_seconds;
u16 padding;
};
struct hpi_profile_msg {
u16 bin_index;
u16 padding;
};
struct hpi_profile_res_open {
u16 max_profiles;
};
struct hpi_profile_res_time {
u32 micro_seconds;
u32 call_count;
u32 max_micro_seconds;
u32 min_micro_seconds;
u16 seconds;
};
struct hpi_profile_res_name {
u8 sz_name[32];
};
struct hpi_profile_res {
union {
struct hpi_profile_res_open o;
struct hpi_profile_res_time t;
struct hpi_profile_res_name n;
} u;
};
struct hpi_message_header {
u16 size; /* total size in bytes */
u8 type; /* HPI_TYPE_MESSAGE */
u8 version; /* message version */
u16 object; /* HPI_OBJ_* */
u16 function; /* HPI_SUBSYS_xxx, HPI_ADAPTER_xxx */
u16 adapter_index; /* the adapter index */
u16 obj_index; /* */
};
struct hpi_message {
/* following fields must match HPI_MESSAGE_HEADER */
u16 size; /* total size in bytes */
u8 type; /* HPI_TYPE_MESSAGE */
u8 version; /* message version */
u16 object; /* HPI_OBJ_* */
u16 function; /* HPI_SUBSYS_xxx, HPI_ADAPTER_xxx */
u16 adapter_index; /* the adapter index */
u16 obj_index; /* */
union {
struct hpi_subsys_msg s;
struct hpi_adapter_msg a;
union hpi_adapterx_msg ax;
struct hpi_stream_msg d;
struct hpi_mixer_msg m;
union hpi_mixerx_msg mx; /* extended mixer; */
struct hpi_control_msg c; /* mixer control; */
/* identical to struct hpi_control_msg,
but field naming is improved */
struct hpi_control_union_msg cu;
struct hpi_controlx_msg cx; /* extended mixer control; */
struct hpi_nvmemory_msg n;
struct hpi_gpio_msg l; /* digital i/o */
struct hpi_watchdog_msg w;
struct hpi_clock_msg t; /* dsp time */
struct hpi_profile_msg p;
struct hpi_async_msg as;
char fixed_size[32];
} u;
};
#define HPI_MESSAGE_SIZE_BY_OBJECT { \
sizeof(struct hpi_message_header) , /* default, no object type 0 */ \
sizeof(struct hpi_message_header) + sizeof(struct hpi_subsys_msg),\
sizeof(struct hpi_message_header) + sizeof(union hpi_adapterx_msg),\
sizeof(struct hpi_message_header) + sizeof(struct hpi_stream_msg),\
sizeof(struct hpi_message_header) + sizeof(struct hpi_stream_msg),\
sizeof(struct hpi_message_header) + sizeof(struct hpi_mixer_msg),\
sizeof(struct hpi_message_header) , /* no node message */ \
sizeof(struct hpi_message_header) + sizeof(struct hpi_control_msg),\
sizeof(struct hpi_message_header) + sizeof(struct hpi_nvmemory_msg),\
sizeof(struct hpi_message_header) + sizeof(struct hpi_gpio_msg),\
sizeof(struct hpi_message_header) + sizeof(struct hpi_watchdog_msg),\
sizeof(struct hpi_message_header) + sizeof(struct hpi_clock_msg),\
sizeof(struct hpi_message_header) + sizeof(struct hpi_profile_msg),\
sizeof(struct hpi_message_header) + sizeof(struct hpi_controlx_msg),\
sizeof(struct hpi_message_header) + sizeof(struct hpi_async_msg) \
}
struct hpi_response_header {
u16 size;
u8 type; /* HPI_TYPE_RESPONSE */
u8 version; /* response version */
u16 object; /* HPI_OBJ_* */
u16 function; /* HPI_SUBSYS_xxx, HPI_ADAPTER_xxx */
u16 error; /* HPI_ERROR_xxx */
u16 specific_error; /* adapter specific error */
};
struct hpi_response {
/* following fields must match HPI_RESPONSE_HEADER */
u16 size;
u8 type; /* HPI_TYPE_RESPONSE */
u8 version; /* response version */
u16 object; /* HPI_OBJ_* */
u16 function; /* HPI_SUBSYS_xxx, HPI_ADAPTER_xxx */
u16 error; /* HPI_ERROR_xxx */
u16 specific_error; /* adapter specific error */
union {
struct hpi_subsys_res s;
struct hpi_adapter_res a;
union hpi_adapterx_res ax;
struct hpi_stream_res d;
struct hpi_mixer_res m;
union hpi_mixerx_res mx; /* extended mixer; */
struct hpi_control_res c; /* mixer control; */
/* identical to hpi_control_res, but field naming is improved */
union hpi_control_union_res cu;
struct hpi_controlx_res cx; /* extended mixer control; */
struct hpi_nvmemory_res n;
struct hpi_gpio_res l; /* digital i/o */
struct hpi_watchdog_res w;
struct hpi_clock_res t; /* dsp time */
struct hpi_profile_res p;
struct hpi_async_res as;
u8 bytes[52];
} u;
};
#define HPI_RESPONSE_SIZE_BY_OBJECT { \
sizeof(struct hpi_response_header) ,/* default, no object type 0 */ \
sizeof(struct hpi_response_header) + sizeof(struct hpi_subsys_res),\
sizeof(struct hpi_response_header) + sizeof(union hpi_adapterx_res),\
sizeof(struct hpi_response_header) + sizeof(struct hpi_stream_res),\
sizeof(struct hpi_response_header) + sizeof(struct hpi_stream_res),\
sizeof(struct hpi_response_header) + sizeof(struct hpi_mixer_res),\
sizeof(struct hpi_response_header) , /* no node response */ \
sizeof(struct hpi_response_header) + sizeof(struct hpi_control_res),\
sizeof(struct hpi_response_header) + sizeof(struct hpi_nvmemory_res),\
sizeof(struct hpi_response_header) + sizeof(struct hpi_gpio_res),\
sizeof(struct hpi_response_header) + sizeof(struct hpi_watchdog_res),\
sizeof(struct hpi_response_header) + sizeof(struct hpi_clock_res),\
sizeof(struct hpi_response_header) + sizeof(struct hpi_profile_res),\
sizeof(struct hpi_response_header) + sizeof(struct hpi_controlx_res),\
sizeof(struct hpi_response_header) + sizeof(struct hpi_async_res) \
}
/*********************** version 1 message/response *****************************/
#define HPINET_ETHERNET_DATA_SIZE (1500)
#define HPINET_IP_HDR_SIZE (20)
#define HPINET_IP_DATA_SIZE (HPINET_ETHERNET_DATA_SIZE - HPINET_IP_HDR_SIZE)
#define HPINET_UDP_HDR_SIZE (8)
#define HPINET_UDP_DATA_SIZE (HPINET_IP_DATA_SIZE - HPINET_UDP_HDR_SIZE)
#define HPINET_ASI_HDR_SIZE (2)
#define HPINET_ASI_DATA_SIZE (HPINET_UDP_DATA_SIZE - HPINET_ASI_HDR_SIZE)
#define HPI_MAX_PAYLOAD_SIZE (HPINET_ASI_DATA_SIZE - 2)
/* New style message/response, but still V0 compatible */
struct hpi_msg_adapter_get_info {
struct hpi_message_header h;
};
struct hpi_res_adapter_get_info {
struct hpi_response_header h; /*v0 */
struct hpi_adapter_res p;
};
/* padding is so these are same size as v0 hpi_message */
struct hpi_msg_adapter_query_flash {
struct hpi_message_header h;
u32 offset;
u8 pad_to_version0_size[sizeof(struct hpi_message) - /* V0 res */
sizeof(struct hpi_message_header) - 1 * sizeof(u32)];
};
/* padding is so these are same size as v0 hpi_response */
struct hpi_res_adapter_query_flash {
struct hpi_response_header h;
u32 checksum;
u32 length;
u32 version;
u8 pad_to_version0_size[sizeof(struct hpi_response) - /* V0 res */
sizeof(struct hpi_response_header) - 3 * sizeof(u32)];
};
struct hpi_msg_adapter_start_flash {
struct hpi_message_header h;
u32 offset;
u32 length;
u32 key;
u8 pad_to_version0_size[sizeof(struct hpi_message) - /* V0 res */
sizeof(struct hpi_message_header) - 3 * sizeof(u32)];
};
struct hpi_res_adapter_start_flash {
struct hpi_response_header h;
u8 pad_to_version0_size[sizeof(struct hpi_response) - /* V0 res */
sizeof(struct hpi_response_header)];
};
struct hpi_msg_adapter_program_flash_payload {
u32 checksum;
u16 sequence;
u16 length;
u16 offset; /**< offset from start of msg to data */
u16 unused;
/* ensure sizeof(header + payload) == sizeof(hpi_message_V0)
because old firmware expects data after message of this size */
u8 pad_to_version0_size[sizeof(struct hpi_message) - /* V0 message */
sizeof(struct hpi_message_header) - sizeof(u32) -
4 * sizeof(u16)];
};
struct hpi_msg_adapter_program_flash {
struct hpi_message_header h;
struct hpi_msg_adapter_program_flash_payload p;
u32 data[256];
};
struct hpi_res_adapter_program_flash {
struct hpi_response_header h;
u16 sequence;
u8 pad_to_version0_size[sizeof(struct hpi_response) - /* V0 res */
sizeof(struct hpi_response_header) - sizeof(u16)];
};
#if 1
#define hpi_message_header_v1 hpi_message_header
#define hpi_response_header_v1 hpi_response_header
#else
/* V1 headers in Addition to v0 headers */
struct hpi_message_header_v1 {
struct hpi_message_header h0;
/* struct {
} h1; */
};
struct hpi_response_header_v1 {
struct hpi_response_header h0;
struct {
u16 adapter_index; /* the adapter index */
u16 obj_index; /* object index */
} h1;
};
#endif
/* STRV HPI Packet */
struct hpi_msg_strv {
struct hpi_message_header h;
struct hpi_entity strv;
};
struct hpi_res_strv {
struct hpi_response_header h;
struct hpi_entity strv;
};
#define MIN_STRV_PACKET_SIZE sizeof(struct hpi_res_strv)
struct hpi_msg_payload_v0 {
struct hpi_message_header h;
union {
struct hpi_subsys_msg s;
struct hpi_adapter_msg a;
union hpi_adapterx_msg ax;
struct hpi_stream_msg d;
struct hpi_mixer_msg m;
union hpi_mixerx_msg mx;
struct hpi_control_msg c;
struct hpi_control_union_msg cu;
struct hpi_controlx_msg cx;
struct hpi_nvmemory_msg n;
struct hpi_gpio_msg l;
struct hpi_watchdog_msg w;
struct hpi_clock_msg t;
struct hpi_profile_msg p;
struct hpi_async_msg as;
} u;
};
struct hpi_res_payload_v0 {
struct hpi_response_header h;
union {
struct hpi_subsys_res s;
struct hpi_adapter_res a;
union hpi_adapterx_res ax;
struct hpi_stream_res d;
struct hpi_mixer_res m;
union hpi_mixerx_res mx;
struct hpi_control_res c;
union hpi_control_union_res cu;
struct hpi_controlx_res cx;
struct hpi_nvmemory_res n;
struct hpi_gpio_res l;
struct hpi_watchdog_res w;
struct hpi_clock_res t;
struct hpi_profile_res p;
struct hpi_async_res as;
} u;
};
union hpi_message_buffer_v1 {
struct hpi_message m0; /* version 0 */
struct hpi_message_header_v1 h;
unsigned char buf[HPI_MAX_PAYLOAD_SIZE];
};
union hpi_response_buffer_v1 {
struct hpi_response r0; /* version 0 */
struct hpi_response_header_v1 h;
unsigned char buf[HPI_MAX_PAYLOAD_SIZE];
};
compile_time_assert((sizeof(union hpi_message_buffer_v1) <=
HPI_MAX_PAYLOAD_SIZE), message_buffer_ok);
compile_time_assert((sizeof(union hpi_response_buffer_v1) <=
HPI_MAX_PAYLOAD_SIZE), response_buffer_ok);
/*////////////////////////////////////////////////////////////////////////// */
/* declarations for compact control calls */
struct hpi_control_defn {
u8 type;
u8 channels;
u8 src_node_type;
u8 src_node_index;
u8 dest_node_type;
u8 dest_node_index;
};
/*////////////////////////////////////////////////////////////////////////// */
/* declarations for control caching (internal to HPI<->DSP interaction) */
/** A compact representation of (part of) a controls state.
Used for efficient transfer of the control state
between DSP and host or across a network
*/
struct hpi_control_cache_info {
/** one of HPI_CONTROL_* */
u8 control_type;
/** The total size of cached information in 32-bit words. */
u8 size_in32bit_words;
/** The original index of the control on the DSP */
u16 control_index;
};
struct hpi_control_cache_single {
struct hpi_control_cache_info i;
union {
struct { /* volume */
u16 an_log[2];
} v;
struct { /* peak meter */
u16 an_log_peak[2];
u16 an_logRMS[2];
} p;
struct { /* channel mode */
u16 mode;
} m;
struct { /* multiplexer */
u16 source_node_type;
u16 source_node_index;
} x;
struct { /* level/trim */
u16 an_log[2];
} l;
struct { /* tuner - partial caching.
some attributes go to the DSP. */
u32 freq_ink_hz;
u16 band;
u16 level;
} t;
struct { /* AESEBU rx status */
u32 error_status;
u32 source;
} aes3rx;
struct { /* AESEBU tx */
u32 format;
} aes3tx;
struct { /* tone detector */
u16 state;
} tone;
struct { /* silence detector */
u32 state;
u32 count;
} silence;
struct { /* sample clock */
u16 source;
u16 source_index;
u32 sample_rate;
} clk;
struct { /* microphone control */
u16 state;
} phantom_power;
struct { /* generic control */
u32 dw1;
u32 dw2;
} g;
} u;
};
struct hpi_control_cache_pad {
struct hpi_control_cache_info i;
u32 field_valid_flags;
u8 c_channel[8];
u8 c_artist[40];
u8 c_title[40];
u8 c_comment[200];
u32 pTY;
u32 pI;
u32 traffic_supported;
u32 traffic_anouncement;
};
/*/////////////////////////////////////////////////////////////////////////// */
/* declarations for 2^N sized FIFO buffer (internal to HPI<->DSP interaction) */
struct hpi_fifo_buffer {
u32 size;
u32 dSP_index;
u32 host_index;
};
#ifndef DISABLE_PRAGMA_PACK1
#pragma pack(pop)
#endif
/* skip host side function declarations for DSP
compile and documentation extraction */
char hpi_handle_object(const u32 handle);
void hpi_handle_to_indexes(const u32 handle, u16 *pw_adapter_index,
u16 *pw_object_index);
u32 hpi_indexes_to_handle(const char c_object, const u16 adapter_index,
const u16 object_index);
/*////////////////////////////////////////////////////////////////////////// */
/* main HPI entry point */
hpi_handler_func hpi_send_recv;
/* UDP message */
void hpi_send_recvUDP(struct hpi_message *phm, struct hpi_response *phr,
const unsigned int timeout);
/* used in PnP OS/driver */
u16 hpi_subsys_create_adapter(const struct hpi_hsubsys *ph_subsys,
const struct hpi_resource *p_resource, u16 *pw_adapter_index);
u16 hpi_subsys_delete_adapter(const struct hpi_hsubsys *ph_subsys,
u16 adapter_index);
u16 hpi_outstream_host_buffer_get_info(const struct hpi_hsubsys *ph_subsys,
u32 h_outstream, u8 **pp_buffer,
struct hpi_hostbuffer_status **pp_status);
u16 hpi_instream_host_buffer_get_info(const struct hpi_hsubsys *ph_subsys,
u32 h_instream, u8 **pp_buffer,
struct hpi_hostbuffer_status **pp_status);
u16 hpi_adapter_restart(u16 adapter_index);
/*
The following 3 functions were last declared in header files for
driver 3.10. HPI_ControlQuery() used to be the recommended way
of getting a volume range. Declared here for binary asihpi32.dll
compatibility.
*/
void hpi_format_to_msg(struct hpi_msg_format *pMF,
const struct hpi_format *pF);
void hpi_stream_response_to_legacy(struct hpi_stream_res *pSR);
/*////////////////////////////////////////////////////////////////////////// */
/* declarations for individual HPI entry points */
hpi_handler_func HPI_1000;
hpi_handler_func HPI_6000;
hpi_handler_func HPI_6205;
hpi_handler_func HPI_COMMON;
#endif /* _HPI_INTERNAL_H_ */
/******************************************************************************
AudioScience HPI driver
Copyright (C) 1997-2010 AudioScience Inc. <support@audioscience.com>
This program is free software; you can redistribute it and/or modify
it under the terms of version 2 of the GNU General Public License as
published by the Free Software Foundation;
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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
\file hpicmn.c
Common functions used by hpixxxx.c modules
(C) Copyright AudioScience Inc. 1998-2003
*******************************************************************************/
#define SOURCEFILE_NAME "hpicmn.c"
#include "hpi_internal.h"
#include "hpidebug.h"
#include "hpicmn.h"
struct hpi_adapters_list {
struct hpios_spinlock list_lock;
struct hpi_adapter_obj adapter[HPI_MAX_ADAPTERS];
u16 gw_num_adapters;
};
static struct hpi_adapters_list adapters;
/**
* Given an HPI Message that was sent out and a response that was received,
* validate that the response has the correct fields filled in,
* i.e ObjectType, Function etc
**/
u16 hpi_validate_response(struct hpi_message *phm, struct hpi_response *phr)
{
u16 error = 0;
if ((phr->type != HPI_TYPE_RESPONSE)
|| (phr->object != phm->object)
|| (phr->function != phm->function))
error = HPI_ERROR_INVALID_RESPONSE;
return error;
}
u16 hpi_add_adapter(struct hpi_adapter_obj *pao)
{
u16 retval = 0;
/*HPI_ASSERT(pao->wAdapterType); */
hpios_alistlock_lock(&adapters);
if (pao->index >= HPI_MAX_ADAPTERS) {
retval = HPI_ERROR_BAD_ADAPTER_NUMBER;
goto unlock;
}
if (adapters.adapter[pao->index].adapter_type) {
{
retval = HPI_DUPLICATE_ADAPTER_NUMBER;
goto unlock;
}
}
adapters.adapter[pao->index] = *pao;
hpios_dsplock_init(&adapters.adapter[pao->index]);
adapters.gw_num_adapters++;
unlock:
hpios_alistlock_un_lock(&adapters);
return retval;
}
void hpi_delete_adapter(struct hpi_adapter_obj *pao)
{
memset(pao, 0, sizeof(struct hpi_adapter_obj));
hpios_alistlock_lock(&adapters);
adapters.gw_num_adapters--; /* dec the number of adapters */
hpios_alistlock_un_lock(&adapters);
}
/**
* FindAdapter returns a pointer to the struct hpi_adapter_obj with
* index wAdapterIndex in an HPI_ADAPTERS_LIST structure.
*
*/
struct hpi_adapter_obj *hpi_find_adapter(u16 adapter_index)
{
struct hpi_adapter_obj *pao = NULL;
if (adapter_index >= HPI_MAX_ADAPTERS) {
HPI_DEBUG_LOG(VERBOSE, "find_adapter invalid index %d ",
adapter_index);
return NULL;
}
pao = &adapters.adapter[adapter_index];
if (pao->adapter_type != 0) {
/*
HPI_DEBUG_LOG(VERBOSE, "Found adapter index %d\n",
wAdapterIndex);
*/
return pao;
} else {
/*
HPI_DEBUG_LOG(VERBOSE, "No adapter index %d\n",
wAdapterIndex);
*/
return NULL;
}
}
/**
*
* wipe an HPI_ADAPTERS_LIST structure.
*
**/
static void wipe_adapter_list(void
)
{
memset(&adapters, 0, sizeof(adapters));
}
/**
* SubSysGetAdapters fills awAdapterList in an struct hpi_response structure
* with all adapters in the given HPI_ADAPTERS_LIST.
*
*/
static void subsys_get_adapters(struct hpi_response *phr)
{
/* fill in the response adapter array with the position */
/* identified by the adapter number/index of the adapters in */
/* this HPI */
/* i.e. if we have an A120 with it's jumper set to */
/* Adapter Number 2 then put an Adapter type A120 in the */
/* array in position 1 */
/* NOTE: AdapterNumber is 1..N, Index is 0..N-1 */
/* input: NONE */
/* output: wNumAdapters */
/* awAdapter[] */
/* */
short i;
struct hpi_adapter_obj *pao = NULL;
HPI_DEBUG_LOG(VERBOSE, "subsys_get_adapters\n");
/* for each adapter, place it's type in the position of the array */
/* corresponding to it's adapter number */
for (i = 0; i < adapters.gw_num_adapters; i++) {
pao = &adapters.adapter[i];
if (phr->u.s.aw_adapter_list[pao->index] != 0) {
phr->error = HPI_DUPLICATE_ADAPTER_NUMBER;
phr->specific_error = pao->index;
return;
}
phr->u.s.aw_adapter_list[pao->index] = pao->adapter_type;
}
phr->u.s.num_adapters = adapters.gw_num_adapters;
phr->error = 0; /* the function completed OK; */
}
static unsigned int control_cache_alloc_check(struct hpi_control_cache *pC)
{
unsigned int i;
int cached = 0;
if (!pC)
return 0;
if ((!pC->init) && (pC->p_cache != NULL) && (pC->control_count)
&& (pC->cache_size_in_bytes)
) {
u32 *p_master_cache;
pC->init = 1;
p_master_cache = (u32 *)pC->p_cache;
HPI_DEBUG_LOG(VERBOSE, "check %d controls\n",
pC->control_count);
for (i = 0; i < pC->control_count; i++) {
struct hpi_control_cache_info *info =
(struct hpi_control_cache_info *)
p_master_cache;
if (info->control_type) {
pC->p_info[i] = info;
cached++;
} else
pC->p_info[i] = NULL;
if (info->size_in32bit_words)
p_master_cache += info->size_in32bit_words;
else
p_master_cache +=
sizeof(struct
hpi_control_cache_single) /
sizeof(u32);
HPI_DEBUG_LOG(VERBOSE,
"cached %d, pinfo %p index %d type %d\n",
cached, pC->p_info[i], info->control_index,
info->control_type);
}
/*
We didn't find anything to cache, so try again later !
*/
if (!cached)
pC->init = 0;
}
return pC->init;
}
/** Find a control.
*/
static short find_control(struct hpi_message *phm,
struct hpi_control_cache *p_cache, struct hpi_control_cache_info **pI,
u16 *pw_control_index)
{
*pw_control_index = phm->obj_index;
if (!control_cache_alloc_check(p_cache)) {
HPI_DEBUG_LOG(VERBOSE,
"control_cache_alloc_check() failed. adap%d ci%d\n",
phm->adapter_index, *pw_control_index);
return 0;
}
*pI = p_cache->p_info[*pw_control_index];
if (!*pI) {
HPI_DEBUG_LOG(VERBOSE, "uncached adap %d, control %d\n",
phm->adapter_index, *pw_control_index);
return 0;
} else {
HPI_DEBUG_LOG(VERBOSE, "find_control() type %d\n",
(*pI)->control_type);
}
return 1;
}
/** Used by the kernel driver to figure out if a buffer needs mapping.
*/
short hpi_check_buffer_mapping(struct hpi_control_cache *p_cache,
struct hpi_message *phm, void **p, unsigned int *pN)
{
*pN = 0;
*p = NULL;
if ((phm->function == HPI_CONTROL_GET_STATE)
&& (phm->object == HPI_OBJ_CONTROLEX)
) {
u16 control_index;
struct hpi_control_cache_info *pI;
if (!find_control(phm, p_cache, &pI, &control_index))
return 0;
}
return 0;
}
/* allow unified treatment of several string fields within struct */
#define HPICMN_PAD_OFS_AND_SIZE(m) {\
offsetof(struct hpi_control_cache_pad, m), \
sizeof(((struct hpi_control_cache_pad *)(NULL))->m) }
struct pad_ofs_size {
unsigned int offset;
unsigned int field_size;
};
static struct pad_ofs_size pad_desc[] = {
HPICMN_PAD_OFS_AND_SIZE(c_channel), /* HPI_PAD_CHANNEL_NAME */
HPICMN_PAD_OFS_AND_SIZE(c_artist), /* HPI_PAD_ARTIST */
HPICMN_PAD_OFS_AND_SIZE(c_title), /* HPI_PAD_TITLE */
HPICMN_PAD_OFS_AND_SIZE(c_comment), /* HPI_PAD_COMMENT */
};
/** CheckControlCache checks the cache and fills the struct hpi_response
* accordingly. It returns one if a cache hit occurred, zero otherwise.
*/
short hpi_check_control_cache(struct hpi_control_cache *p_cache,
struct hpi_message *phm, struct hpi_response *phr)
{
short found = 1;
u16 control_index;
struct hpi_control_cache_info *pI;
struct hpi_control_cache_single *pC;
struct hpi_control_cache_pad *p_pad;
if (!find_control(phm, p_cache, &pI, &control_index))
return 0;
phr->error = 0;
/* pC is the default cached control strucure. May be cast to
something else in the following switch statement.
*/
pC = (struct hpi_control_cache_single *)pI;
p_pad = (struct hpi_control_cache_pad *)pI;
switch (pI->control_type) {
case HPI_CONTROL_METER:
if (phm->u.c.attribute == HPI_METER_PEAK) {
phr->u.c.an_log_value[0] = pC->u.p.an_log_peak[0];
phr->u.c.an_log_value[1] = pC->u.p.an_log_peak[1];
} else if (phm->u.c.attribute == HPI_METER_RMS) {
phr->u.c.an_log_value[0] = pC->u.p.an_logRMS[0];
phr->u.c.an_log_value[1] = pC->u.p.an_logRMS[1];
} else
found = 0;
break;
case HPI_CONTROL_VOLUME:
if (phm->u.c.attribute == HPI_VOLUME_GAIN) {
phr->u.c.an_log_value[0] = pC->u.v.an_log[0];
phr->u.c.an_log_value[1] = pC->u.v.an_log[1];
} else
found = 0;
break;
case HPI_CONTROL_MULTIPLEXER:
if (phm->u.c.attribute == HPI_MULTIPLEXER_SOURCE) {
phr->u.c.param1 = pC->u.x.source_node_type;
phr->u.c.param2 = pC->u.x.source_node_index;
} else {
found = 0;
}
break;
case HPI_CONTROL_CHANNEL_MODE:
if (phm->u.c.attribute == HPI_CHANNEL_MODE_MODE)
phr->u.c.param1 = pC->u.m.mode;
else
found = 0;
break;
case HPI_CONTROL_LEVEL:
if (phm->u.c.attribute == HPI_LEVEL_GAIN) {
phr->u.c.an_log_value[0] = pC->u.l.an_log[0];
phr->u.c.an_log_value[1] = pC->u.l.an_log[1];
} else
found = 0;
break;
case HPI_CONTROL_TUNER:
{
struct hpi_control_cache_single *pCT =
(struct hpi_control_cache_single *)pI;
if (phm->u.c.attribute == HPI_TUNER_FREQ)
phr->u.c.param1 = pCT->u.t.freq_ink_hz;
else if (phm->u.c.attribute == HPI_TUNER_BAND)
phr->u.c.param1 = pCT->u.t.band;
else if ((phm->u.c.attribute == HPI_TUNER_LEVEL)
&& (phm->u.c.param1 ==
HPI_TUNER_LEVEL_AVERAGE))
phr->u.c.param1 = pCT->u.t.level;
else
found = 0;
}
break;
case HPI_CONTROL_AESEBU_RECEIVER:
if (phm->u.c.attribute == HPI_AESEBURX_ERRORSTATUS)
phr->u.c.param1 = pC->u.aes3rx.error_status;
else if (phm->u.c.attribute == HPI_AESEBURX_FORMAT)
phr->u.c.param1 = pC->u.aes3rx.source;
else
found = 0;
break;
case HPI_CONTROL_AESEBU_TRANSMITTER:
if (phm->u.c.attribute == HPI_AESEBUTX_FORMAT)
phr->u.c.param1 = pC->u.aes3tx.format;
else
found = 0;
break;
case HPI_CONTROL_TONEDETECTOR:
if (phm->u.c.attribute == HPI_TONEDETECTOR_STATE)
phr->u.c.param1 = pC->u.tone.state;
else
found = 0;
break;
case HPI_CONTROL_SILENCEDETECTOR:
if (phm->u.c.attribute == HPI_SILENCEDETECTOR_STATE) {
phr->u.c.param1 = pC->u.silence.state;
phr->u.c.param2 = pC->u.silence.count;
} else
found = 0;
break;
case HPI_CONTROL_MICROPHONE:
if (phm->u.c.attribute == HPI_MICROPHONE_PHANTOM_POWER)
phr->u.c.param1 = pC->u.phantom_power.state;
else
found = 0;
break;
case HPI_CONTROL_SAMPLECLOCK:
if (phm->u.c.attribute == HPI_SAMPLECLOCK_SOURCE)
phr->u.c.param1 = pC->u.clk.source;
else if (phm->u.c.attribute == HPI_SAMPLECLOCK_SOURCE_INDEX) {
if (pC->u.clk.source_index ==
HPI_ERROR_ILLEGAL_CACHE_VALUE) {
phr->u.c.param1 = 0;
phr->error = HPI_ERROR_INVALID_OPERATION;
} else
phr->u.c.param1 = pC->u.clk.source_index;
} else if (phm->u.c.attribute == HPI_SAMPLECLOCK_SAMPLERATE)
phr->u.c.param1 = pC->u.clk.sample_rate;
else
found = 0;
break;
case HPI_CONTROL_PAD:
if (!(p_pad->field_valid_flags & (1 <<
HPI_CTL_ATTR_INDEX(phm->u.c.
attribute)))) {
phr->error = HPI_ERROR_INVALID_CONTROL_ATTRIBUTE;
break;
}
if (phm->u.c.attribute == HPI_PAD_PROGRAM_ID)
phr->u.c.param1 = p_pad->pI;
else if (phm->u.c.attribute == HPI_PAD_PROGRAM_TYPE)
phr->u.c.param1 = p_pad->pTY;
else {
unsigned int index =
HPI_CTL_ATTR_INDEX(phm->u.c.attribute) - 1;
unsigned int offset = phm->u.c.param1;
unsigned int pad_string_len, field_size;
char *pad_string;
unsigned int tocopy;
HPI_DEBUG_LOG(VERBOSE, "PADS HPI_PADS_ %d\n",
phm->u.c.attribute);
if (index > ARRAY_SIZE(pad_desc) - 1) {
phr->error =
HPI_ERROR_INVALID_CONTROL_ATTRIBUTE;
break;
}
pad_string = ((char *)p_pad) + pad_desc[index].offset;
field_size = pad_desc[index].field_size;
/* Ensure null terminator */
pad_string[field_size - 1] = 0;
pad_string_len = strlen(pad_string) + 1;
if (offset > pad_string_len) {
phr->error = HPI_ERROR_INVALID_CONTROL_VALUE;
break;
}
tocopy = pad_string_len - offset;
if (tocopy > sizeof(phr->u.cu.chars8.sz_data))
tocopy = sizeof(phr->u.cu.chars8.sz_data);
HPI_DEBUG_LOG(VERBOSE,
"PADS memcpy(%d), offset %d \n", tocopy,
offset);
memcpy(phr->u.cu.chars8.sz_data, &pad_string[offset],
tocopy);
phr->u.cu.chars8.remaining_chars =
pad_string_len - offset - tocopy;
}
break;
default:
found = 0;
break;
}
if (found)
HPI_DEBUG_LOG(VERBOSE,
"cached adap %d, ctl %d, type %d, attr %d\n",
phm->adapter_index, pI->control_index,
pI->control_type, phm->u.c.attribute);
else
HPI_DEBUG_LOG(VERBOSE,
"uncached adap %d, ctl %d, ctl type %d\n",
phm->adapter_index, pI->control_index,
pI->control_type);
if (found)
phr->size =
sizeof(struct hpi_response_header) +
sizeof(struct hpi_control_res);
return found;
}
/** Updates the cache with Set values.
Only update if no error.
Volume and Level return the limited values in the response, so use these
Multiplexer does so use sent values
*/
void hpi_sync_control_cache(struct hpi_control_cache *p_cache,
struct hpi_message *phm, struct hpi_response *phr)
{
u16 control_index;
struct hpi_control_cache_single *pC;
struct hpi_control_cache_info *pI;
if (!find_control(phm, p_cache, &pI, &control_index))
return;
/* pC is the default cached control strucure.
May be cast to something else in the following switch statement.
*/
pC = (struct hpi_control_cache_single *)pI;
switch (pI->control_type) {
case HPI_CONTROL_VOLUME:
if (phm->u.c.attribute == HPI_VOLUME_GAIN) {
pC->u.v.an_log[0] = phr->u.c.an_log_value[0];
pC->u.v.an_log[1] = phr->u.c.an_log_value[1];
}
break;
case HPI_CONTROL_MULTIPLEXER:
/* mux does not return its setting on Set command. */
if (phr->error)
return;
if (phm->u.c.attribute == HPI_MULTIPLEXER_SOURCE) {
pC->u.x.source_node_type = (u16)phm->u.c.param1;
pC->u.x.source_node_index = (u16)phm->u.c.param2;
}
break;
case HPI_CONTROL_CHANNEL_MODE:
/* mode does not return its setting on Set command. */
if (phr->error)
return;
if (phm->u.c.attribute == HPI_CHANNEL_MODE_MODE)
pC->u.m.mode = (u16)phm->u.c.param1;
break;
case HPI_CONTROL_LEVEL:
if (phm->u.c.attribute == HPI_LEVEL_GAIN) {
pC->u.v.an_log[0] = phr->u.c.an_log_value[0];
pC->u.v.an_log[1] = phr->u.c.an_log_value[1];
}
break;
case HPI_CONTROL_MICROPHONE:
if (phm->u.c.attribute == HPI_MICROPHONE_PHANTOM_POWER)
pC->u.phantom_power.state = (u16)phm->u.c.param1;
break;
case HPI_CONTROL_AESEBU_TRANSMITTER:
if (phr->error)
return;
if (phm->u.c.attribute == HPI_AESEBUTX_FORMAT)
pC->u.aes3tx.format = phm->u.c.param1;
break;
case HPI_CONTROL_AESEBU_RECEIVER:
if (phr->error)
return;
if (phm->u.c.attribute == HPI_AESEBURX_FORMAT)
pC->u.aes3rx.source = phm->u.c.param1;
break;
case HPI_CONTROL_SAMPLECLOCK:
if (phr->error)
return;
if (phm->u.c.attribute == HPI_SAMPLECLOCK_SOURCE)
pC->u.clk.source = (u16)phm->u.c.param1;
else if (phm->u.c.attribute == HPI_SAMPLECLOCK_SOURCE_INDEX)
pC->u.clk.source_index = (u16)phm->u.c.param1;
else if (phm->u.c.attribute == HPI_SAMPLECLOCK_SAMPLERATE)
pC->u.clk.sample_rate = phm->u.c.param1;
break;
default:
break;
}
}
struct hpi_control_cache *hpi_alloc_control_cache(const u32
number_of_controls, const u32 size_in_bytes,
struct hpi_control_cache_info *pDSP_control_buffer)
{
struct hpi_control_cache *p_cache =
kmalloc(sizeof(*p_cache), GFP_KERNEL);
p_cache->cache_size_in_bytes = size_in_bytes;
p_cache->control_count = number_of_controls;
p_cache->p_cache =
(struct hpi_control_cache_single *)pDSP_control_buffer;
p_cache->init = 0;
p_cache->p_info =
kmalloc(sizeof(*p_cache->p_info) * p_cache->control_count,
GFP_KERNEL);
return p_cache;
}
void hpi_free_control_cache(struct hpi_control_cache *p_cache)
{
if ((p_cache->init) && (p_cache->p_info)) {
kfree(p_cache->p_info);
p_cache->p_info = NULL;
p_cache->init = 0;
kfree(p_cache);
}
}
static void subsys_message(struct hpi_message *phm, struct hpi_response *phr)
{
switch (phm->function) {
case HPI_SUBSYS_OPEN:
case HPI_SUBSYS_CLOSE:
case HPI_SUBSYS_DRIVER_UNLOAD:
phr->error = 0;
break;
case HPI_SUBSYS_DRIVER_LOAD:
wipe_adapter_list();
hpios_alistlock_init(&adapters);
phr->error = 0;
break;
case HPI_SUBSYS_GET_INFO:
subsys_get_adapters(phr);
break;
case HPI_SUBSYS_CREATE_ADAPTER:
case HPI_SUBSYS_DELETE_ADAPTER:
phr->error = 0;
break;
default:
phr->error = HPI_ERROR_INVALID_FUNC;
break;
}
}
void HPI_COMMON(struct hpi_message *phm, struct hpi_response *phr)
{
switch (phm->type) {
case HPI_TYPE_MESSAGE:
switch (phm->object) {
case HPI_OBJ_SUBSYSTEM:
subsys_message(phm, phr);
break;
}
break;
default:
phr->error = HPI_ERROR_INVALID_TYPE;
break;
}
}
/**
AudioScience HPI driver
Copyright (C) 1997-2010 AudioScience Inc. <support@audioscience.com>
This program is free software; you can redistribute it and/or modify
it under the terms of version 2 of the GNU General Public License as
published by the Free Software Foundation;
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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
struct hpi_adapter_obj {
struct hpi_pci pci; /* PCI info - bus#,dev#,address etc */
u16 adapter_type; /* ASI6701 etc */
u16 index; /* */
u16 open; /* =1 when adapter open */
u16 mixer_open;
struct hpios_spinlock dsp_lock;
u16 dsp_crashed;
u16 has_control_cache;
void *priv;
};
struct hpi_control_cache {
u32 init; /**< indicates whether the
structures are initialized */
u32 control_count;
u32 cache_size_in_bytes;
struct hpi_control_cache_info
**p_info; /**< pointer to allocated memory of
lookup pointers. */
struct hpi_control_cache_single
*p_cache; /**< pointer to DSP's control cache. */
};
struct hpi_adapter_obj *hpi_find_adapter(u16 adapter_index);
u16 hpi_add_adapter(struct hpi_adapter_obj *pao);
void hpi_delete_adapter(struct hpi_adapter_obj *pao);
short hpi_check_control_cache(struct hpi_control_cache *pC,
struct hpi_message *phm, struct hpi_response *phr);
struct hpi_control_cache *hpi_alloc_control_cache(const u32
number_of_controls, const u32 size_in_bytes,
struct hpi_control_cache_info
*pDSP_control_buffer);
void hpi_free_control_cache(struct hpi_control_cache *p_cache);
void hpi_sync_control_cache(struct hpi_control_cache *pC,
struct hpi_message *phm, struct hpi_response *phr);
u16 hpi_validate_response(struct hpi_message *phm, struct hpi_response *phr);
short hpi_check_buffer_mapping(struct hpi_control_cache *p_cache,
struct hpi_message *phm, void **p, unsigned int *pN);
/************************************************************************
AudioScience HPI driver
Copyright (C) 1997-2010 AudioScience Inc. <support@audioscience.com>
This program is free software; you can redistribute it and/or modify
it under the terms of version 2 of the GNU General Public License as
published by the Free Software Foundation;
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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
Debug macro translation.
************************************************************************/
#include "hpi_internal.h"
#include "hpidebug.h"
/* Debug level; 0 quiet; 1 informative, 2 debug, 3 verbose debug. */
int hpi_debug_level = HPI_DEBUG_LEVEL_DEFAULT;
void hpi_debug_init(void)
{
printk(KERN_INFO "debug start\n");
}
int hpi_debug_level_set(int level)
{
int old_level;
old_level = hpi_debug_level;
hpi_debug_level = level;
return old_level;
}
int hpi_debug_level_get(void)
{
return hpi_debug_level;
}
#ifdef HPIOS_DEBUG_PRINT
/* implies OS has no printf-like function */
#include <stdarg.h>
void hpi_debug_printf(char *fmt, ...)
{
va_list arglist;
char buffer[128];
va_start(arglist, fmt);
if (buffer[0])
HPIOS_DEBUG_PRINT(buffer);
va_end(arglist);
}
#endif
struct treenode {
void *array;
unsigned int num_elements;
};
#define make_treenode_from_array(nodename, array) \
static void *tmp_strarray_##nodename[] = array; \
static struct treenode nodename = { \
&tmp_strarray_##nodename, \
ARRAY_SIZE(tmp_strarray_##nodename) \
};
#define get_treenode_elem(node_ptr, idx, type) \
(&(*((type *)(node_ptr)->array)[idx]))
make_treenode_from_array(hpi_control_type_strings, HPI_CONTROL_TYPE_STRINGS)
make_treenode_from_array(hpi_subsys_strings, HPI_SUBSYS_STRINGS)
make_treenode_from_array(hpi_adapter_strings, HPI_ADAPTER_STRINGS)
make_treenode_from_array(hpi_istream_strings, HPI_ISTREAM_STRINGS)
make_treenode_from_array(hpi_ostream_strings, HPI_OSTREAM_STRINGS)
make_treenode_from_array(hpi_mixer_strings, HPI_MIXER_STRINGS)
make_treenode_from_array(hpi_node_strings,
{
"NODE is invalid object"})
make_treenode_from_array(hpi_control_strings, HPI_CONTROL_STRINGS)
make_treenode_from_array(hpi_nvmemory_strings, HPI_OBJ_STRINGS)
make_treenode_from_array(hpi_digitalio_strings, HPI_DIGITALIO_STRINGS)
make_treenode_from_array(hpi_watchdog_strings, HPI_WATCHDOG_STRINGS)
make_treenode_from_array(hpi_clock_strings, HPI_CLOCK_STRINGS)
make_treenode_from_array(hpi_profile_strings, HPI_PROFILE_STRINGS)
make_treenode_from_array(hpi_asyncevent_strings, HPI_ASYNCEVENT_STRINGS)
#define HPI_FUNCTION_STRINGS \
{ \
&hpi_subsys_strings,\
&hpi_adapter_strings,\
&hpi_ostream_strings,\
&hpi_istream_strings,\
&hpi_mixer_strings,\
&hpi_node_strings,\
&hpi_control_strings,\
&hpi_nvmemory_strings,\
&hpi_digitalio_strings,\
&hpi_watchdog_strings,\
&hpi_clock_strings,\
&hpi_profile_strings,\
&hpi_control_strings, \
&hpi_asyncevent_strings \
};
make_treenode_from_array(hpi_function_strings, HPI_FUNCTION_STRINGS)
compile_time_assert(HPI_OBJ_MAXINDEX == 14, obj_list_doesnt_match);
static char *hpi_function_string(unsigned int function)
{
unsigned int object;
struct treenode *tmp;
object = function / HPI_OBJ_FUNCTION_SPACING;
function = function - object * HPI_OBJ_FUNCTION_SPACING;
if (object == 0 || object == HPI_OBJ_NODE
|| object > hpi_function_strings.num_elements)
return "invalid object";
tmp = get_treenode_elem(&hpi_function_strings, object - 1,
struct treenode *);
if (function == 0 || function > tmp->num_elements)
return "invalid function";
return get_treenode_elem(tmp, function - 1, char *);
}
void hpi_debug_message(struct hpi_message *phm, char *sz_fileline)
{
if (phm) {
if ((phm->object <= HPI_OBJ_MAXINDEX) && phm->object) {
u16 index = 0;
u16 attrib = 0;
int is_control = 0;
index = phm->obj_index;
switch (phm->object) {
case HPI_OBJ_ADAPTER:
case HPI_OBJ_PROFILE:
break;
case HPI_OBJ_MIXER:
if (phm->function ==
HPI_MIXER_GET_CONTROL_BY_INDEX)
index = phm->u.m.control_index;
break;
case HPI_OBJ_OSTREAM:
case HPI_OBJ_ISTREAM:
break;
case HPI_OBJ_CONTROLEX:
case HPI_OBJ_CONTROL:
if (phm->version == 1)
attrib = HPI_CTL_ATTR(UNIVERSAL, 1);
else
attrib = phm->u.c.attribute;
is_control = 1;
break;
default:
break;
}
if (is_control && (attrib & 0xFF00)) {
int control_type = (attrib & 0xFF00) >> 8;
int attr_index = HPI_CTL_ATTR_INDEX(attrib);
/* note the KERN facility level
is in szFileline already */
printk("%s adapter %d %s "
"ctrl_index x%04x %s %d\n",
sz_fileline, phm->adapter_index,
hpi_function_string(phm->function),
index,
get_treenode_elem
(&hpi_control_type_strings,
control_type, char *),
attr_index);
} else
printk("%s adapter %d %s "
"idx x%04x attr x%04x \n",
sz_fileline, phm->adapter_index,
hpi_function_string(phm->function),
index, attrib);
} else {
printk("adap=%d, invalid obj=%d, func=0x%x\n",
phm->adapter_index, phm->object,
phm->function);
}
} else
printk(KERN_ERR
"NULL message pointer to hpi_debug_message!\n");
}
void hpi_debug_data(u16 *pdata, u32 len)
{
u32 i;
int j;
int k;
int lines;
int cols = 8;
lines = (len + cols - 1) / cols;
if (lines > 8)
lines = 8;
for (i = 0, j = 0; j < lines; j++) {
printk(KERN_DEBUG "%p:", (pdata + i));
for (k = 0; k < cols && i < len; i++, k++)
printk("%s%04x", k == 0 ? "" : " ", pdata[i]);
printk("\n");
}
}
/*****************************************************************************
AudioScience HPI driver
Copyright (C) 1997-2010 AudioScience Inc. <support@audioscience.com>
This program is free software; you can redistribute it and/or modify
it under the terms of version 2 of the GNU General Public License as
published by the Free Software Foundation;
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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
Debug macros.
*****************************************************************************/
#ifndef _HPIDEBUG_H
#define _HPIDEBUG_H
#include "hpi_internal.h"
/* Define debugging levels. */
enum { HPI_DEBUG_LEVEL_ERROR = 0, /* always log errors */
HPI_DEBUG_LEVEL_WARNING = 1,
HPI_DEBUG_LEVEL_NOTICE = 2,
HPI_DEBUG_LEVEL_INFO = 3,
HPI_DEBUG_LEVEL_DEBUG = 4,
HPI_DEBUG_LEVEL_VERBOSE = 5 /* same printk level as DEBUG */
};
#define HPI_DEBUG_LEVEL_DEFAULT HPI_DEBUG_LEVEL_NOTICE
/* an OS can define an extra flag string that is appended to
the start of each message, eg see hpios_linux.h */
#ifdef SOURCEFILE_NAME
#define FILE_LINE SOURCEFILE_NAME ":" __stringify(__LINE__) " "
#else
#define FILE_LINE __FILE__ ":" __stringify(__LINE__) " "
#endif
#if defined(HPI_DEBUG) && defined(_WINDOWS)
#define HPI_DEBUGBREAK() debug_break()
#else
#define HPI_DEBUGBREAK()
#endif
#define HPI_DEBUG_ASSERT(expression) \
do { \
if (!(expression)) {\
printk(KERN_ERR FILE_LINE\
"ASSERT " __stringify(expression));\
HPI_DEBUGBREAK();\
} \
} while (0)
#define HPI_DEBUG_LOG(level, ...) \
do { \
if (hpi_debug_level >= HPI_DEBUG_LEVEL_##level) { \
printk(HPI_DEBUG_FLAG_##level \
FILE_LINE __VA_ARGS__); \
} \
} while (0)
void hpi_debug_init(void);
int hpi_debug_level_set(int level);
int hpi_debug_level_get(void);
/* needed by Linux driver for dynamic debug level changes */
extern int hpi_debug_level;
void hpi_debug_message(struct hpi_message *phm, char *sz_fileline);
void hpi_debug_data(u16 *pdata, u32 len);
#define HPI_DEBUG_DATA(pdata, len) \
do { \
if (hpi_debug_level >= HPI_DEBUG_LEVEL_VERBOSE) \
hpi_debug_data(pdata, len); \
} while (0)
#define HPI_DEBUG_MESSAGE(level, phm) \
do { \
if (hpi_debug_level >= HPI_DEBUG_LEVEL_##level) { \
hpi_debug_message(phm,HPI_DEBUG_FLAG_##level \
FILE_LINE __stringify(level));\
} \
} while (0)
#define HPI_DEBUG_RESPONSE(phr) \
do { \
if ((hpi_debug_level >= HPI_DEBUG_LEVEL_DEBUG) && (phr->error))\
HPI_DEBUG_LOG(ERROR, \
"HPI response - error# %d\n", \
phr->error); \
else if (hpi_debug_level >= HPI_DEBUG_LEVEL_VERBOSE) \
HPI_DEBUG_LOG(VERBOSE, "HPI response OK\n");\
} while (0)
#ifndef compile_time_assert
#define compile_time_assert(cond, msg) \
typedef char msg[(cond) ? 1 : -1]
#endif
/* check that size is exactly some number */
#define function_count_check(sym, size) \
compile_time_assert((sym##_FUNCTION_COUNT) == (size),\
strings_match_defs_##sym)
/* These strings should be generated using a macro which defines
the corresponding symbol values. */
#define HPI_OBJ_STRINGS \
{ \
"HPI_OBJ_SUBSYSTEM", \
"HPI_OBJ_ADAPTER", \
"HPI_OBJ_OSTREAM", \
"HPI_OBJ_ISTREAM", \
"HPI_OBJ_MIXER", \
"HPI_OBJ_NODE", \
"HPI_OBJ_CONTROL", \
"HPI_OBJ_NVMEMORY", \
"HPI_OBJ_DIGITALIO", \
"HPI_OBJ_WATCHDOG", \
"HPI_OBJ_CLOCK", \
"HPI_OBJ_PROFILE", \
"HPI_OBJ_CONTROLEX" \
}
#define HPI_SUBSYS_STRINGS \
{ \
"HPI_SUBSYS_OPEN", \
"HPI_SUBSYS_GET_VERSION", \
"HPI_SUBSYS_GET_INFO", \
"HPI_SUBSYS_FIND_ADAPTERS", \
"HPI_SUBSYS_CREATE_ADAPTER",\
"HPI_SUBSYS_CLOSE", \
"HPI_SUBSYS_DELETE_ADAPTER", \
"HPI_SUBSYS_DRIVER_LOAD", \
"HPI_SUBSYS_DRIVER_UNLOAD", \
"HPI_SUBSYS_READ_PORT_8", \
"HPI_SUBSYS_WRITE_PORT_8", \
"HPI_SUBSYS_GET_NUM_ADAPTERS",\
"HPI_SUBSYS_GET_ADAPTER", \
"HPI_SUBSYS_SET_NETWORK_INTERFACE"\
}
function_count_check(HPI_SUBSYS, 14);
#define HPI_ADAPTER_STRINGS \
{ \
"HPI_ADAPTER_OPEN", \
"HPI_ADAPTER_CLOSE", \
"HPI_ADAPTER_GET_INFO", \
"HPI_ADAPTER_GET_ASSERT", \
"HPI_ADAPTER_TEST_ASSERT", \
"HPI_ADAPTER_SET_MODE", \
"HPI_ADAPTER_GET_MODE", \
"HPI_ADAPTER_ENABLE_CAPABILITY",\
"HPI_ADAPTER_SELFTEST", \
"HPI_ADAPTER_FIND_OBJECT", \
"HPI_ADAPTER_QUERY_FLASH", \
"HPI_ADAPTER_START_FLASH", \
"HPI_ADAPTER_PROGRAM_FLASH", \
"HPI_ADAPTER_SET_PROPERTY", \
"HPI_ADAPTER_GET_PROPERTY", \
"HPI_ADAPTER_ENUM_PROPERTY", \
"HPI_ADAPTER_MODULE_INFO", \
"HPI_ADAPTER_DEBUG_READ" \
}
function_count_check(HPI_ADAPTER, 18);
#define HPI_OSTREAM_STRINGS \
{ \
"HPI_OSTREAM_OPEN", \
"HPI_OSTREAM_CLOSE", \
"HPI_OSTREAM_WRITE", \
"HPI_OSTREAM_START", \
"HPI_OSTREAM_STOP", \
"HPI_OSTREAM_RESET", \
"HPI_OSTREAM_GET_INFO", \
"HPI_OSTREAM_QUERY_FORMAT", \
"HPI_OSTREAM_DATA", \
"HPI_OSTREAM_SET_VELOCITY", \
"HPI_OSTREAM_SET_PUNCHINOUT", \
"HPI_OSTREAM_SINEGEN", \
"HPI_OSTREAM_ANC_RESET", \
"HPI_OSTREAM_ANC_GET_INFO", \
"HPI_OSTREAM_ANC_READ", \
"HPI_OSTREAM_SET_TIMESCALE",\
"HPI_OSTREAM_SET_FORMAT", \
"HPI_OSTREAM_HOSTBUFFER_ALLOC", \
"HPI_OSTREAM_HOSTBUFFER_FREE", \
"HPI_OSTREAM_GROUP_ADD",\
"HPI_OSTREAM_GROUP_GETMAP", \
"HPI_OSTREAM_GROUP_RESET", \
"HPI_OSTREAM_HOSTBUFFER_GET_INFO", \
"HPI_OSTREAM_WAIT_START", \
}
function_count_check(HPI_OSTREAM, 24);
#define HPI_ISTREAM_STRINGS \
{ \
"HPI_ISTREAM_OPEN", \
"HPI_ISTREAM_CLOSE", \
"HPI_ISTREAM_SET_FORMAT", \
"HPI_ISTREAM_READ", \
"HPI_ISTREAM_START", \
"HPI_ISTREAM_STOP", \
"HPI_ISTREAM_RESET", \
"HPI_ISTREAM_GET_INFO", \
"HPI_ISTREAM_QUERY_FORMAT", \
"HPI_ISTREAM_ANC_RESET", \
"HPI_ISTREAM_ANC_GET_INFO", \
"HPI_ISTREAM_ANC_WRITE", \
"HPI_ISTREAM_HOSTBUFFER_ALLOC",\
"HPI_ISTREAM_HOSTBUFFER_FREE", \
"HPI_ISTREAM_GROUP_ADD", \
"HPI_ISTREAM_GROUP_GETMAP", \
"HPI_ISTREAM_GROUP_RESET", \
"HPI_ISTREAM_HOSTBUFFER_GET_INFO", \
"HPI_ISTREAM_WAIT_START", \
}
function_count_check(HPI_ISTREAM, 19);
#define HPI_MIXER_STRINGS \
{ \
"HPI_MIXER_OPEN", \
"HPI_MIXER_CLOSE", \
"HPI_MIXER_GET_INFO", \
"HPI_MIXER_GET_NODE_INFO", \
"HPI_MIXER_GET_CONTROL", \
"HPI_MIXER_SET_CONNECTION", \
"HPI_MIXER_GET_CONNECTIONS", \
"HPI_MIXER_GET_CONTROL_BY_INDEX", \
"HPI_MIXER_GET_CONTROL_ARRAY_BY_INDEX", \
"HPI_MIXER_GET_CONTROL_MULTIPLE_VALUES", \
"HPI_MIXER_STORE", \
}
function_count_check(HPI_MIXER, 11);
#define HPI_CONTROL_STRINGS \
{ \
"HPI_CONTROL_GET_INFO", \
"HPI_CONTROL_GET_STATE", \
"HPI_CONTROL_SET_STATE" \
}
function_count_check(HPI_CONTROL, 3);
#define HPI_NVMEMORY_STRINGS \
{ \
"HPI_NVMEMORY_OPEN", \
"HPI_NVMEMORY_READ_BYTE", \
"HPI_NVMEMORY_WRITE_BYTE" \
}
function_count_check(HPI_NVMEMORY, 3);
#define HPI_DIGITALIO_STRINGS \
{ \
"HPI_GPIO_OPEN", \
"HPI_GPIO_READ_BIT", \
"HPI_GPIO_WRITE_BIT", \
"HPI_GPIO_READ_ALL", \
"HPI_GPIO_WRITE_STATUS"\
}
function_count_check(HPI_GPIO, 5);
#define HPI_WATCHDOG_STRINGS \
{ \
"HPI_WATCHDOG_OPEN", \
"HPI_WATCHDOG_SET_TIME", \
"HPI_WATCHDOG_PING" \
}
#define HPI_CLOCK_STRINGS \
{ \
"HPI_CLOCK_OPEN", \
"HPI_CLOCK_SET_TIME", \
"HPI_CLOCK_GET_TIME" \
}
#define HPI_PROFILE_STRINGS \
{ \
"HPI_PROFILE_OPEN_ALL", \
"HPI_PROFILE_START_ALL", \
"HPI_PROFILE_STOP_ALL", \
"HPI_PROFILE_GET", \
"HPI_PROFILE_GET_IDLECOUNT", \
"HPI_PROFILE_GET_NAME", \
"HPI_PROFILE_GET_UTILIZATION" \
}
function_count_check(HPI_PROFILE, 7);
#define HPI_ASYNCEVENT_STRINGS \
{ \
"HPI_ASYNCEVENT_OPEN",\
"HPI_ASYNCEVENT_CLOSE ",\
"HPI_ASYNCEVENT_WAIT",\
"HPI_ASYNCEVENT_GETCOUNT",\
"HPI_ASYNCEVENT_GET",\
"HPI_ASYNCEVENT_SENDEVENTS"\
}
function_count_check(HPI_ASYNCEVENT, 6);
#define HPI_CONTROL_TYPE_STRINGS \
{ \
"null control", \
"HPI_CONTROL_CONNECTION", \
"HPI_CONTROL_VOLUME", \
"HPI_CONTROL_METER", \
"HPI_CONTROL_MUTE", \
"HPI_CONTROL_MULTIPLEXER", \
"HPI_CONTROL_AESEBU_TRANSMITTER", \
"HPI_CONTROL_AESEBU_RECEIVER", \
"HPI_CONTROL_LEVEL", \
"HPI_CONTROL_TUNER", \
"HPI_CONTROL_ONOFFSWITCH", \
"HPI_CONTROL_VOX", \
"HPI_CONTROL_AES18_TRANSMITTER", \
"HPI_CONTROL_AES18_RECEIVER", \
"HPI_CONTROL_AES18_BLOCKGENERATOR", \
"HPI_CONTROL_CHANNEL_MODE", \
"HPI_CONTROL_BITSTREAM", \
"HPI_CONTROL_SAMPLECLOCK", \
"HPI_CONTROL_MICROPHONE", \
"HPI_CONTROL_PARAMETRIC_EQ", \
"HPI_CONTROL_COMPANDER", \
"HPI_CONTROL_COBRANET", \
"HPI_CONTROL_TONE_DETECT", \
"HPI_CONTROL_SILENCE_DETECT", \
"HPI_CONTROL_PAD", \
"HPI_CONTROL_SRC" ,\
"HPI_CONTROL_UNIVERSAL" \
}
compile_time_assert((HPI_CONTROL_LAST_INDEX + 1 == 27),
controltype_strings_match_defs);
#define HPI_SOURCENODE_STRINGS \
{ \
"no source", \
"HPI_SOURCENODE_OSTREAM", \
"HPI_SOURCENODE_LINEIN", \
"HPI_SOURCENODE_AESEBU_IN", \
"HPI_SOURCENODE_TUNER", \
"HPI_SOURCENODE_RF", \
"HPI_SOURCENODE_CLOCK_SOURCE", \
"HPI_SOURCENODE_RAW_BITSTREAM", \
"HPI_SOURCENODE_MICROPHONE", \
"HPI_SOURCENODE_COBRANET", \
"HPI_SOURCENODE_ANALOG", \
"HPI_SOURCENODE_ADAPTER" \
}
compile_time_assert((HPI_SOURCENODE_LAST_INDEX - HPI_SOURCENODE_BASE + 1) ==
(12), sourcenode_strings_match_defs);
#define HPI_DESTNODE_STRINGS \
{ \
"no destination", \
"HPI_DESTNODE_ISTREAM", \
"HPI_DESTNODE_LINEOUT", \
"HPI_DESTNODE_AESEBU_OUT", \
"HPI_DESTNODE_RF", \
"HPI_DESTNODE_SPEAKER", \
"HPI_DESTNODE_COBRANET", \
"HPI_DESTNODE_ANALOG" \
}
compile_time_assert((HPI_DESTNODE_LAST_INDEX - HPI_DESTNODE_BASE + 1) == (8),
destnode_strings_match_defs);
#define HPI_CONTROL_CHANNEL_MODE_STRINGS \
{ \
"XXX HPI_CHANNEL_MODE_ERROR XXX", \
"HPI_CHANNEL_MODE_NORMAL", \
"HPI_CHANNEL_MODE_SWAP", \
"HPI_CHANNEL_MODE_LEFT_ONLY", \
"HPI_CHANNEL_MODE_RIGHT_ONLY" \
}
#endif /* _HPIDEBUG_H */
/***********************************************************************/
/*!
AudioScience HPI driver
Copyright (C) 1997-2010 AudioScience Inc. <support@audioscience.com>
This program is free software; you can redistribute it and/or modify
it under the terms of version 2 of the GNU General Public License as
published by the Free Software Foundation;
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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
\file
Functions for reading DSP code to load into DSP
(Linux only:) If DSPCODE_FIRMWARE_LOADER is defined, code is read using
hotplug firmware loader from individual dsp code files
If neither of the above is defined, code is read from linked arrays.
DSPCODE_ARRAY is defined.
HPI_INCLUDE_**** must be defined
and the appropriate hzz?????.c or hex?????.c linked in
*/
/***********************************************************************/
#define SOURCEFILE_NAME "hpidspcd.c"
#include "hpidspcd.h"
#include "hpidebug.h"
/**
Header structure for binary dsp code file (see asidsp.doc)
This structure must match that used in s2bin.c for generation of asidsp.bin
*/
#ifndef DISABLE_PRAGMA_PACK1
#pragma pack(push, 1)
#endif
struct code_header {
u32 size;
char type[4];
u32 adapter;
u32 version;
u32 crc;
};
#ifndef DISABLE_PRAGMA_PACK1
#pragma pack(pop)
#endif
#define HPI_VER_DECIMAL ((int)(HPI_VER_MAJOR(HPI_VER) * 10000 + \
HPI_VER_MINOR(HPI_VER) * 100 + HPI_VER_RELEASE(HPI_VER)))
/***********************************************************************/
#include "linux/pci.h"
/*-------------------------------------------------------------------*/
short hpi_dsp_code_open(u32 adapter, struct dsp_code *ps_dsp_code,
u32 *pos_error_code)
{
const struct firmware *ps_firmware = ps_dsp_code->ps_firmware;
struct code_header header;
char fw_name[20];
int err;
sprintf(fw_name, "asihpi/dsp%04x.bin", adapter);
HPI_DEBUG_LOG(INFO, "requesting firmware for %s\n", fw_name);
err = request_firmware(&ps_firmware, fw_name,
&ps_dsp_code->ps_dev->dev);
if (err != 0) {
HPI_DEBUG_LOG(ERROR, "%d, request_firmware failed for %s\n",
err, fw_name);
goto error1;
}
if (ps_firmware->size < sizeof(header)) {
HPI_DEBUG_LOG(ERROR, "header size too small %s\n", fw_name);
goto error2;
}
memcpy(&header, ps_firmware->data, sizeof(header));
if (header.adapter != adapter) {
HPI_DEBUG_LOG(ERROR, "adapter type incorrect %4x != %4x\n",
header.adapter, adapter);
goto error2;
}
if (header.size != ps_firmware->size) {
HPI_DEBUG_LOG(ERROR, "code size wrong %d != %ld\n",
header.size, (unsigned long)ps_firmware->size);
goto error2;
}
if (header.version / 10000 != HPI_VER_DECIMAL / 10000) {
HPI_DEBUG_LOG(ERROR,
"firmware major version mismatch "
"DSP image %d != driver %d\n", header.version,
HPI_VER_DECIMAL);
goto error2;
}
if (header.version != HPI_VER_DECIMAL) {
HPI_DEBUG_LOG(WARNING,
"version mismatch DSP image %d != driver %d\n",
header.version, HPI_VER_DECIMAL);
/* goto error2; still allow driver to load */
}
HPI_DEBUG_LOG(INFO, "dsp code %s opened\n", fw_name);
ps_dsp_code->ps_firmware = ps_firmware;
ps_dsp_code->block_length = header.size / sizeof(u32);
ps_dsp_code->word_count = sizeof(header) / sizeof(u32);
ps_dsp_code->version = header.version;
ps_dsp_code->crc = header.crc;
return 0;
error2:
release_firmware(ps_firmware);
error1:
ps_dsp_code->ps_firmware = NULL;
ps_dsp_code->block_length = 0;
return HPI_ERROR_DSP_FILE_NOT_FOUND;
}
/*-------------------------------------------------------------------*/
void hpi_dsp_code_close(struct dsp_code *ps_dsp_code)
{
if (ps_dsp_code->ps_firmware != NULL) {
HPI_DEBUG_LOG(DEBUG, "dsp code closed\n");
release_firmware(ps_dsp_code->ps_firmware);
ps_dsp_code->ps_firmware = NULL;
}
}
/*-------------------------------------------------------------------*/
void hpi_dsp_code_rewind(struct dsp_code *ps_dsp_code)
{
/* Go back to start of data, after header */
ps_dsp_code->word_count = sizeof(struct code_header) / sizeof(u32);
}
/*-------------------------------------------------------------------*/
short hpi_dsp_code_read_word(struct dsp_code *ps_dsp_code, u32 *pword)
{
if (ps_dsp_code->word_count + 1 > ps_dsp_code->block_length)
return (HPI_ERROR_DSP_FILE_FORMAT);
*pword = ((u32 *)(ps_dsp_code->ps_firmware->data))[ps_dsp_code->
word_count];
ps_dsp_code->word_count++;
return 0;
}
/*-------------------------------------------------------------------*/
short hpi_dsp_code_read_block(size_t words_requested,
struct dsp_code *ps_dsp_code, u32 **ppblock)
{
if (ps_dsp_code->word_count + words_requested >
ps_dsp_code->block_length)
return HPI_ERROR_DSP_FILE_FORMAT;
*ppblock =
((u32 *)(ps_dsp_code->ps_firmware->data)) +
ps_dsp_code->word_count;
ps_dsp_code->word_count += words_requested;
return 0;
}
/***********************************************************************/
/**
AudioScience HPI driver
Copyright (C) 1997-2010 AudioScience Inc. <support@audioscience.com>
This program is free software; you can redistribute it and/or modify
it under the terms of version 2 of the GNU General Public License as
published by the Free Software Foundation;
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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
\file
Functions for reading DSP code to load into DSP
hpi_dspcode_defines HPI DSP code loading method
Define exactly one of these to select how the DSP code is supplied to
the adapter.
End users writing applications that use the HPI interface do not have to
use any of the below defines; they are only necessary for building drivers
HPI_DSPCODE_FILE:
DSP code is supplied as a file that is opened and read from by the driver.
HPI_DSPCODE_FIRMWARE:
DSP code is read using the hotplug firmware loader module.
Only valid when compiling the HPI kernel driver under Linux.
*/
/***********************************************************************/
#ifndef _HPIDSPCD_H_
#define _HPIDSPCD_H_
#include "hpi_internal.h"
#ifndef DISABLE_PRAGMA_PACK1
#pragma pack(push, 1)
#endif
/** Descriptor for dspcode from firmware loader */
struct dsp_code {
/** Firmware descriptor */
const struct firmware *ps_firmware;
struct pci_dev *ps_dev;
/** Expected number of words in the whole dsp code,INCL header */
long int block_length;
/** Number of words read so far */
long int word_count;
/** Version read from dsp code file */
u32 version;
/** CRC read from dsp code file */
u32 crc;
};
#ifndef DISABLE_PRAGMA_PACK1
#pragma pack(pop)
#endif
/** Prepare *psDspCode to refer to the requuested adapter.
Searches the file, or selects the appropriate linked array
\return 0 for success, or error code if requested code is not available
*/
short hpi_dsp_code_open(
/** Code identifier, usually adapter family */
u32 adapter,
/** Pointer to DSP code control structure */
struct dsp_code *ps_dsp_code,
/** Pointer to dword to receive OS specific error code */
u32 *pos_error_code);
/** Close the DSP code file */
void hpi_dsp_code_close(struct dsp_code *ps_dsp_code);
/** Rewind to the beginning of the DSP code file (for verify) */
void hpi_dsp_code_rewind(struct dsp_code *ps_dsp_code);
/** Read one word from the dsp code file
\return 0 for success, or error code if eof, or block length exceeded
*/
short hpi_dsp_code_read_word(struct dsp_code *ps_dsp_code,
/**< DSP code descriptor */
u32 *pword /**< where to store the read word */
);
/** Get a block of dsp code into an internal buffer, and provide a pointer to
that buffer. (If dsp code is already an array in memory, it is referenced,
not copied.)
\return Error if requested number of words are not available
*/
short hpi_dsp_code_read_block(size_t words_requested,
struct dsp_code *ps_dsp_code,
/* Pointer to store (Pointer to code buffer) */
u32 **ppblock);
#endif
This source diff could not be displayed because it is too large. You can view the blob instead.
/******************************************************************************
AudioScience HPI driver
Copyright (C) 1997-2010 AudioScience Inc. <support@audioscience.com>
This program is free software; you can redistribute it and/or modify
it under the terms of version 2 of the GNU General Public License as
published by the Free Software Foundation;
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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
Hardware Programming Interface (HPI) Utility functions.
(C) Copyright AudioScience Inc. 2007
*******************************************************************************/
#include "hpi_internal.h"
#include "hpimsginit.h"
/* The actual message size for each object type */
static u16 msg_size[HPI_OBJ_MAXINDEX + 1] = HPI_MESSAGE_SIZE_BY_OBJECT;
/* The actual response size for each object type */
static u16 res_size[HPI_OBJ_MAXINDEX + 1] = HPI_RESPONSE_SIZE_BY_OBJECT;
/* Flag to enable alternate message type for SSX2 bypass. */
static u16 gwSSX2_bypass;
/** \internal
* Used by ASIO driver to disable SSX2 for a single process
* \param phSubSys Pointer to HPI subsystem handle.
* \param wBypass New bypass setting 0 = off, nonzero = on
* \return Previous bypass setting.
*/
u16 hpi_subsys_ssx2_bypass(const struct hpi_hsubsys *ph_subsys, u16 bypass)
{
u16 old_value = gwSSX2_bypass;
gwSSX2_bypass = bypass;
return old_value;
}
/** \internal
* initialize the HPI message structure
*/
static void hpi_init_message(struct hpi_message *phm, u16 object,
u16 function)
{
memset(phm, 0, sizeof(*phm));
if ((object > 0) && (object <= HPI_OBJ_MAXINDEX))
phm->size = msg_size[object];
else
phm->size = sizeof(*phm);
if (gwSSX2_bypass)
phm->type = HPI_TYPE_SSX2BYPASS_MESSAGE;
else
phm->type = HPI_TYPE_MESSAGE;
phm->object = object;
phm->function = function;
phm->version = 0;
/* Expect adapter index to be set by caller */
}
/** \internal
* initialize the HPI response structure
*/
void hpi_init_response(struct hpi_response *phr, u16 object, u16 function,
u16 error)
{
memset(phr, 0, sizeof(*phr));
phr->type = HPI_TYPE_RESPONSE;
if ((object > 0) && (object <= HPI_OBJ_MAXINDEX))
phr->size = res_size[object];
else
phr->size = sizeof(*phr);
phr->object = object;
phr->function = function;
phr->error = error;
phr->specific_error = 0;
phr->version = 0;
}
void hpi_init_message_response(struct hpi_message *phm,
struct hpi_response *phr, u16 object, u16 function)
{
hpi_init_message(phm, object, function);
/* default error return if the response is
not filled in by the callee */
hpi_init_response(phr, object, function,
HPI_ERROR_PROCESSING_MESSAGE);
}
static void hpi_init_messageV1(struct hpi_message_header *phm, u16 size,
u16 object, u16 function)
{
memset(phm, 0, sizeof(*phm));
if ((object > 0) && (object <= HPI_OBJ_MAXINDEX)) {
phm->size = size;
phm->type = HPI_TYPE_MESSAGE;
phm->object = object;
phm->function = function;
phm->version = 1;
/* Expect adapter index to be set by caller */
}
}
void hpi_init_responseV1(struct hpi_response_header *phr, u16 size,
u16 object, u16 function)
{
memset(phr, 0, sizeof(*phr));
phr->size = size;
phr->version = 1;
phr->type = HPI_TYPE_RESPONSE;
phr->error = HPI_ERROR_PROCESSING_MESSAGE;
}
void hpi_init_message_responseV1(struct hpi_message_header *phm, u16 msg_size,
struct hpi_response_header *phr, u16 res_size, u16 object,
u16 function)
{
hpi_init_messageV1(phm, msg_size, object, function);
hpi_init_responseV1(phr, res_size, object, function);
}
/******************************************************************************
AudioScience HPI driver
Copyright (C) 1997-2010 AudioScience Inc. <support@audioscience.com>
This program is free software; you can redistribute it and/or modify
it under the terms of version 2 of the GNU General Public License as
published by the Free Software Foundation;
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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
Hardware Programming Interface (HPI) Utility functions
(C) Copyright AudioScience Inc. 2007
*******************************************************************************/
/* Initialise response headers, or msg/response pairs.
Note that it is valid to just init a response e.g. when a lower level is preparing
a response to a message.
However, when sending a message, a matching response buffer always must be prepared
*/
void hpi_init_response(struct hpi_response *phr, u16 object, u16 function,
u16 error);
void hpi_init_message_response(struct hpi_message *phm,
struct hpi_response *phr, u16 object, u16 function);
void hpi_init_responseV1(struct hpi_response_header *phr, u16 size,
u16 object, u16 function);
void hpi_init_message_responseV1(struct hpi_message_header *phm, u16 msg_size,
struct hpi_response_header *phr, u16 res_size, u16 object,
u16 function);
/******************************************************************************
AudioScience HPI driver
Copyright (C) 1997-2010 AudioScience Inc. <support@audioscience.com>
This program is free software; you can redistribute it and/or modify
it under the terms of version 2 of the GNU General Public License as
published by the Free Software Foundation;
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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
Extended Message Function With Response Cacheing
(C) Copyright AudioScience Inc. 2002
*****************************************************************************/
#define SOURCEFILE_NAME "hpimsgx.c"
#include "hpi_internal.h"
#include "hpimsginit.h"
#include "hpimsgx.h"
#include "hpidebug.h"
static struct pci_device_id asihpi_pci_tbl[] = {
#include "hpipcida.h"
};
static struct hpios_spinlock msgx_lock;
static hpi_handler_func *hpi_entry_points[HPI_MAX_ADAPTERS];
static hpi_handler_func *hpi_lookup_entry_point_function(const struct hpi_pci
*pci_info)
{
int i;
for (i = 0; asihpi_pci_tbl[i].vendor != 0; i++) {
if (asihpi_pci_tbl[i].vendor != PCI_ANY_ID
&& asihpi_pci_tbl[i].vendor != pci_info->vendor_id)
continue;
if (asihpi_pci_tbl[i].device != PCI_ANY_ID
&& asihpi_pci_tbl[i].device != pci_info->device_id)
continue;
if (asihpi_pci_tbl[i].subvendor != PCI_ANY_ID
&& asihpi_pci_tbl[i].subvendor !=
pci_info->subsys_vendor_id)
continue;
if (asihpi_pci_tbl[i].subdevice != PCI_ANY_ID
&& asihpi_pci_tbl[i].subdevice !=
pci_info->subsys_device_id)
continue;
HPI_DEBUG_LOG(DEBUG, " %x,%lu\n", i,
asihpi_pci_tbl[i].driver_data);
return (hpi_handler_func *) asihpi_pci_tbl[i].driver_data;
}
return NULL;
}
static inline void hw_entry_point(struct hpi_message *phm,
struct hpi_response *phr)
{
hpi_handler_func *ep;
if (phm->adapter_index < HPI_MAX_ADAPTERS) {
ep = (hpi_handler_func *) hpi_entry_points[phm->
adapter_index];
if (ep) {
HPI_DEBUG_MESSAGE(DEBUG, phm);
ep(phm, phr);
HPI_DEBUG_RESPONSE(phr);
return;
}
}
hpi_init_response(phr, phm->object, phm->function,
HPI_ERROR_PROCESSING_MESSAGE);
}
static void adapter_open(struct hpi_message *phm, struct hpi_response *phr);
static void adapter_close(struct hpi_message *phm, struct hpi_response *phr);
static void mixer_open(struct hpi_message *phm, struct hpi_response *phr);
static void mixer_close(struct hpi_message *phm, struct hpi_response *phr);
static void outstream_open(struct hpi_message *phm, struct hpi_response *phr,
void *h_owner);
static void outstream_close(struct hpi_message *phm, struct hpi_response *phr,
void *h_owner);
static void instream_open(struct hpi_message *phm, struct hpi_response *phr,
void *h_owner);
static void instream_close(struct hpi_message *phm, struct hpi_response *phr,
void *h_owner);
static void HPIMSGX__reset(u16 adapter_index);
static u16 HPIMSGX__init(struct hpi_message *phm, struct hpi_response *phr);
static void HPIMSGX__cleanup(u16 adapter_index, void *h_owner);
#ifndef DISABLE_PRAGMA_PACK1
#pragma pack(push, 1)
#endif
struct hpi_subsys_response {
struct hpi_response_header h;
struct hpi_subsys_res s;
};
struct hpi_adapter_response {
struct hpi_response_header h;
struct hpi_adapter_res a;
};
struct hpi_mixer_response {
struct hpi_response_header h;
struct hpi_mixer_res m;
};
struct hpi_stream_response {
struct hpi_response_header h;
struct hpi_stream_res d;
};
struct adapter_info {
u16 type;
u16 num_instreams;
u16 num_outstreams;
};
struct asi_open_state {
int open_flag;
void *h_owner;
};
#ifndef DISABLE_PRAGMA_PACK1
#pragma pack(pop)
#endif
/* Globals */
static struct hpi_adapter_response rESP_HPI_ADAPTER_OPEN[HPI_MAX_ADAPTERS];
static struct hpi_stream_response
rESP_HPI_OSTREAM_OPEN[HPI_MAX_ADAPTERS][HPI_MAX_STREAMS];
static struct hpi_stream_response
rESP_HPI_ISTREAM_OPEN[HPI_MAX_ADAPTERS][HPI_MAX_STREAMS];
static struct hpi_mixer_response rESP_HPI_MIXER_OPEN[HPI_MAX_ADAPTERS];
static struct hpi_subsys_response gRESP_HPI_SUBSYS_FIND_ADAPTERS;
static struct adapter_info aDAPTER_INFO[HPI_MAX_ADAPTERS];
/* use these to keep track of opens from user mode apps/DLLs */
static struct asi_open_state
outstream_user_open[HPI_MAX_ADAPTERS][HPI_MAX_STREAMS];
static struct asi_open_state
instream_user_open[HPI_MAX_ADAPTERS][HPI_MAX_STREAMS];
static void subsys_message(struct hpi_message *phm, struct hpi_response *phr,
void *h_owner)
{
switch (phm->function) {
case HPI_SUBSYS_GET_VERSION:
hpi_init_response(phr, HPI_OBJ_SUBSYSTEM,
HPI_SUBSYS_GET_VERSION, 0);
phr->u.s.version = HPI_VER >> 8; /* return major.minor */
phr->u.s.data = HPI_VER; /* return major.minor.release */
break;
case HPI_SUBSYS_OPEN:
/*do not propagate the message down the chain */
hpi_init_response(phr, HPI_OBJ_SUBSYSTEM, HPI_SUBSYS_OPEN, 0);
break;
case HPI_SUBSYS_CLOSE:
/*do not propagate the message down the chain */
hpi_init_response(phr, HPI_OBJ_SUBSYSTEM, HPI_SUBSYS_CLOSE,
0);
HPIMSGX__cleanup(HPIMSGX_ALLADAPTERS, h_owner);
break;
case HPI_SUBSYS_DRIVER_LOAD:
/* Initialize this module's internal state */
hpios_msgxlock_init(&msgx_lock);
memset(&hpi_entry_points, 0, sizeof(hpi_entry_points));
hpios_locked_mem_init();
/* Init subsys_findadapters response to no-adapters */
HPIMSGX__reset(HPIMSGX_ALLADAPTERS);
hpi_init_response(phr, HPI_OBJ_SUBSYSTEM,
HPI_SUBSYS_DRIVER_LOAD, 0);
/* individual HPIs dont implement driver load */
HPI_COMMON(phm, phr);
break;
case HPI_SUBSYS_DRIVER_UNLOAD:
HPI_COMMON(phm, phr);
HPIMSGX__cleanup(HPIMSGX_ALLADAPTERS, h_owner);
hpios_locked_mem_free_all();
hpi_init_response(phr, HPI_OBJ_SUBSYSTEM,
HPI_SUBSYS_DRIVER_UNLOAD, 0);
return;
case HPI_SUBSYS_GET_INFO:
HPI_COMMON(phm, phr);
break;
case HPI_SUBSYS_FIND_ADAPTERS:
memcpy(phr, &gRESP_HPI_SUBSYS_FIND_ADAPTERS,
sizeof(gRESP_HPI_SUBSYS_FIND_ADAPTERS));
break;
case HPI_SUBSYS_GET_NUM_ADAPTERS:
memcpy(phr, &gRESP_HPI_SUBSYS_FIND_ADAPTERS,
sizeof(gRESP_HPI_SUBSYS_FIND_ADAPTERS));
phr->function = HPI_SUBSYS_GET_NUM_ADAPTERS;
break;
case HPI_SUBSYS_GET_ADAPTER:
{
int count = phm->adapter_index;
int index = 0;
hpi_init_response(phr, HPI_OBJ_SUBSYSTEM,
HPI_SUBSYS_GET_ADAPTER, 0);
/* This is complicated by the fact that we want to
* "skip" 0's in the adapter list.
* First, make sure we are pointing to a
* non-zero adapter type.
*/
while (gRESP_HPI_SUBSYS_FIND_ADAPTERS.
s.aw_adapter_list[index] == 0) {
index++;
if (index >= HPI_MAX_ADAPTERS)
break;
}
while (count) {
/* move on to the next adapter */
index++;
if (index >= HPI_MAX_ADAPTERS)
break;
while (gRESP_HPI_SUBSYS_FIND_ADAPTERS.
s.aw_adapter_list[index] == 0) {
index++;
if (index >= HPI_MAX_ADAPTERS)
break;
}
count--;
}
if (index < HPI_MAX_ADAPTERS) {
phr->u.s.adapter_index = (u16)index;
phr->u.s.aw_adapter_list[0] =
gRESP_HPI_SUBSYS_FIND_ADAPTERS.
s.aw_adapter_list[index];
} else {
phr->u.s.adapter_index = 0;
phr->u.s.aw_adapter_list[0] = 0;
phr->error = HPI_ERROR_BAD_ADAPTER_NUMBER;
}
break;
}
case HPI_SUBSYS_CREATE_ADAPTER:
HPIMSGX__init(phm, phr);
break;
case HPI_SUBSYS_DELETE_ADAPTER:
HPIMSGX__cleanup(phm->adapter_index, h_owner);
{
struct hpi_message hm;
struct hpi_response hr;
/* call to HPI_ADAPTER_CLOSE */
hpi_init_message_response(&hm, &hr, HPI_OBJ_ADAPTER,
HPI_ADAPTER_CLOSE);
hm.adapter_index = phm->adapter_index;
hw_entry_point(&hm, &hr);
}
hw_entry_point(phm, phr);
gRESP_HPI_SUBSYS_FIND_ADAPTERS.s.
aw_adapter_list[phm->adapter_index]
= 0;
hpi_entry_points[phm->adapter_index] = NULL;
break;
default:
hw_entry_point(phm, phr);
break;
}
}
static void adapter_message(struct hpi_message *phm, struct hpi_response *phr,
void *h_owner)
{
switch (phm->function) {
case HPI_ADAPTER_OPEN:
adapter_open(phm, phr);
break;
case HPI_ADAPTER_CLOSE:
adapter_close(phm, phr);
break;
default:
hw_entry_point(phm, phr);
break;
}
}
static void mixer_message(struct hpi_message *phm, struct hpi_response *phr)
{
switch (phm->function) {
case HPI_MIXER_OPEN:
mixer_open(phm, phr);
break;
case HPI_MIXER_CLOSE:
mixer_close(phm, phr);
break;
default:
hw_entry_point(phm, phr);
break;
}
}
static void outstream_message(struct hpi_message *phm,
struct hpi_response *phr, void *h_owner)
{
if (phm->obj_index >= aDAPTER_INFO[phm->adapter_index].num_outstreams) {
hpi_init_response(phr, HPI_OBJ_OSTREAM, phm->function,
HPI_ERROR_INVALID_OBJ_INDEX);
return;
}
switch (phm->function) {
case HPI_OSTREAM_OPEN:
outstream_open(phm, phr, h_owner);
break;
case HPI_OSTREAM_CLOSE:
outstream_close(phm, phr, h_owner);
break;
default:
hw_entry_point(phm, phr);
break;
}
}
static void instream_message(struct hpi_message *phm,
struct hpi_response *phr, void *h_owner)
{
if (phm->obj_index >= aDAPTER_INFO[phm->adapter_index].num_instreams) {
hpi_init_response(phr, HPI_OBJ_ISTREAM, phm->function,
HPI_ERROR_INVALID_OBJ_INDEX);
return;
}
switch (phm->function) {
case HPI_ISTREAM_OPEN:
instream_open(phm, phr, h_owner);
break;
case HPI_ISTREAM_CLOSE:
instream_close(phm, phr, h_owner);
break;
default:
hw_entry_point(phm, phr);
break;
}
}
/* NOTE: HPI_Message() must be defined in the driver as a wrapper for
* HPI_MessageEx so that functions in hpifunc.c compile.
*/
void hpi_send_recv_ex(struct hpi_message *phm, struct hpi_response *phr,
void *h_owner)
{
HPI_DEBUG_MESSAGE(DEBUG, phm);
if (phm->type != HPI_TYPE_MESSAGE) {
hpi_init_response(phr, phm->object, phm->function,
HPI_ERROR_INVALID_TYPE);
return;
}
if (phm->adapter_index >= HPI_MAX_ADAPTERS
&& phm->adapter_index != HPIMSGX_ALLADAPTERS) {
hpi_init_response(phr, phm->object, phm->function,
HPI_ERROR_BAD_ADAPTER_NUMBER);
return;
}
switch (phm->object) {
case HPI_OBJ_SUBSYSTEM:
subsys_message(phm, phr, h_owner);
break;
case HPI_OBJ_ADAPTER:
adapter_message(phm, phr, h_owner);
break;
case HPI_OBJ_MIXER:
mixer_message(phm, phr);
break;
case HPI_OBJ_OSTREAM:
outstream_message(phm, phr, h_owner);
break;
case HPI_OBJ_ISTREAM:
instream_message(phm, phr, h_owner);
break;
default:
hw_entry_point(phm, phr);
break;
}
HPI_DEBUG_RESPONSE(phr);
#if 1
if (phr->error >= HPI_ERROR_BACKEND_BASE) {
void *ep = NULL;
char *ep_name;
HPI_DEBUG_MESSAGE(ERROR, phm);
if (phm->adapter_index < HPI_MAX_ADAPTERS)
ep = hpi_entry_points[phm->adapter_index];
/* Don't need this? Have adapter index in debug info
Know at driver load time index->backend mapping */
if (ep == HPI_6000)
ep_name = "HPI_6000";
else if (ep == HPI_6205)
ep_name = "HPI_6205";
else
ep_name = "unknown";
HPI_DEBUG_LOG(ERROR, "HPI %s response - error# %d\n", ep_name,
phr->error);
if (hpi_debug_level >= HPI_DEBUG_LEVEL_VERBOSE)
hpi_debug_data((u16 *)phm,
sizeof(*phm) / sizeof(u16));
}
#endif
}
static void adapter_open(struct hpi_message *phm, struct hpi_response *phr)
{
HPI_DEBUG_LOG(VERBOSE, "adapter_open\n");
memcpy(phr, &rESP_HPI_ADAPTER_OPEN[phm->adapter_index],
sizeof(rESP_HPI_ADAPTER_OPEN[0]));
}
static void adapter_close(struct hpi_message *phm, struct hpi_response *phr)
{
HPI_DEBUG_LOG(VERBOSE, "adapter_close\n");
hpi_init_response(phr, HPI_OBJ_ADAPTER, HPI_ADAPTER_CLOSE, 0);
}
static void mixer_open(struct hpi_message *phm, struct hpi_response *phr)
{
memcpy(phr, &rESP_HPI_MIXER_OPEN[phm->adapter_index],
sizeof(rESP_HPI_MIXER_OPEN[0]));
}
static void mixer_close(struct hpi_message *phm, struct hpi_response *phr)
{
hpi_init_response(phr, HPI_OBJ_MIXER, HPI_MIXER_CLOSE, 0);
}
static void instream_open(struct hpi_message *phm, struct hpi_response *phr,
void *h_owner)
{
struct hpi_message hm;
struct hpi_response hr;
hpi_init_response(phr, HPI_OBJ_ISTREAM, HPI_ISTREAM_OPEN, 0);
hpios_msgxlock_lock(&msgx_lock);
if (instream_user_open[phm->adapter_index][phm->obj_index].open_flag)
phr->error = HPI_ERROR_OBJ_ALREADY_OPEN;
else if (rESP_HPI_ISTREAM_OPEN[phm->adapter_index]
[phm->obj_index].h.error)
memcpy(phr,
&rESP_HPI_ISTREAM_OPEN[phm->adapter_index][phm->
obj_index],
sizeof(rESP_HPI_ISTREAM_OPEN[0][0]));
else {
instream_user_open[phm->adapter_index][phm->
obj_index].open_flag = 1;
hpios_msgxlock_un_lock(&msgx_lock);
/* issue a reset */
hpi_init_message_response(&hm, &hr, HPI_OBJ_ISTREAM,
HPI_ISTREAM_RESET);
hm.adapter_index = phm->adapter_index;
hm.obj_index = phm->obj_index;
hw_entry_point(&hm, &hr);
hpios_msgxlock_lock(&msgx_lock);
if (hr.error) {
instream_user_open[phm->adapter_index][phm->
obj_index].open_flag = 0;
phr->error = hr.error;
} else {
instream_user_open[phm->adapter_index][phm->
obj_index].open_flag = 1;
instream_user_open[phm->adapter_index][phm->
obj_index].h_owner = h_owner;
memcpy(phr,
&rESP_HPI_ISTREAM_OPEN[phm->adapter_index]
[phm->obj_index],
sizeof(rESP_HPI_ISTREAM_OPEN[0][0]));
}
}
hpios_msgxlock_un_lock(&msgx_lock);
}
static void instream_close(struct hpi_message *phm, struct hpi_response *phr,
void *h_owner)
{
struct hpi_message hm;
struct hpi_response hr;
hpi_init_response(phr, HPI_OBJ_ISTREAM, HPI_ISTREAM_CLOSE, 0);
hpios_msgxlock_lock(&msgx_lock);
if (h_owner ==
instream_user_open[phm->adapter_index][phm->
obj_index].h_owner) {
/* HPI_DEBUG_LOG(INFO,"closing adapter %d "
"instream %d owned by %p\n",
phm->wAdapterIndex, phm->wObjIndex, hOwner); */
instream_user_open[phm->adapter_index][phm->
obj_index].h_owner = NULL;
hpios_msgxlock_un_lock(&msgx_lock);
/* issue a reset */
hpi_init_message_response(&hm, &hr, HPI_OBJ_ISTREAM,
HPI_ISTREAM_RESET);
hm.adapter_index = phm->adapter_index;
hm.obj_index = phm->obj_index;
hw_entry_point(&hm, &hr);
hpios_msgxlock_lock(&msgx_lock);
if (hr.error) {
instream_user_open[phm->adapter_index][phm->
obj_index].h_owner = h_owner;
phr->error = hr.error;
} else {
instream_user_open[phm->adapter_index][phm->
obj_index].open_flag = 0;
instream_user_open[phm->adapter_index][phm->
obj_index].h_owner = NULL;
}
} else {
HPI_DEBUG_LOG(WARNING,
"%p trying to close %d instream %d owned by %p\n",
h_owner, phm->adapter_index, phm->obj_index,
instream_user_open[phm->adapter_index][phm->
obj_index].h_owner);
phr->error = HPI_ERROR_OBJ_NOT_OPEN;
}
hpios_msgxlock_un_lock(&msgx_lock);
}
static void outstream_open(struct hpi_message *phm, struct hpi_response *phr,
void *h_owner)
{
struct hpi_message hm;
struct hpi_response hr;
hpi_init_response(phr, HPI_OBJ_OSTREAM, HPI_OSTREAM_OPEN, 0);
hpios_msgxlock_lock(&msgx_lock);
if (outstream_user_open[phm->adapter_index][phm->obj_index].open_flag)
phr->error = HPI_ERROR_OBJ_ALREADY_OPEN;
else if (rESP_HPI_OSTREAM_OPEN[phm->adapter_index]
[phm->obj_index].h.error)
memcpy(phr,
&rESP_HPI_OSTREAM_OPEN[phm->adapter_index][phm->
obj_index],
sizeof(rESP_HPI_OSTREAM_OPEN[0][0]));
else {
outstream_user_open[phm->adapter_index][phm->
obj_index].open_flag = 1;
hpios_msgxlock_un_lock(&msgx_lock);
/* issue a reset */
hpi_init_message_response(&hm, &hr, HPI_OBJ_OSTREAM,
HPI_OSTREAM_RESET);
hm.adapter_index = phm->adapter_index;
hm.obj_index = phm->obj_index;
hw_entry_point(&hm, &hr);
hpios_msgxlock_lock(&msgx_lock);
if (hr.error) {
outstream_user_open[phm->adapter_index][phm->
obj_index].open_flag = 0;
phr->error = hr.error;
} else {
outstream_user_open[phm->adapter_index][phm->
obj_index].open_flag = 1;
outstream_user_open[phm->adapter_index][phm->
obj_index].h_owner = h_owner;
memcpy(phr,
&rESP_HPI_OSTREAM_OPEN[phm->adapter_index]
[phm->obj_index],
sizeof(rESP_HPI_OSTREAM_OPEN[0][0]));
}
}
hpios_msgxlock_un_lock(&msgx_lock);
}
static void outstream_close(struct hpi_message *phm, struct hpi_response *phr,
void *h_owner)
{
struct hpi_message hm;
struct hpi_response hr;
hpi_init_response(phr, HPI_OBJ_OSTREAM, HPI_OSTREAM_CLOSE, 0);
hpios_msgxlock_lock(&msgx_lock);
if (h_owner ==
outstream_user_open[phm->adapter_index][phm->
obj_index].h_owner) {
/* HPI_DEBUG_LOG(INFO,"closing adapter %d "
"outstream %d owned by %p\n",
phm->wAdapterIndex, phm->wObjIndex, hOwner); */
outstream_user_open[phm->adapter_index][phm->
obj_index].h_owner = NULL;
hpios_msgxlock_un_lock(&msgx_lock);
/* issue a reset */
hpi_init_message_response(&hm, &hr, HPI_OBJ_OSTREAM,
HPI_OSTREAM_RESET);
hm.adapter_index = phm->adapter_index;
hm.obj_index = phm->obj_index;
hw_entry_point(&hm, &hr);
hpios_msgxlock_lock(&msgx_lock);
if (hr.error) {
outstream_user_open[phm->adapter_index][phm->
obj_index].h_owner = h_owner;
phr->error = hr.error;
} else {
outstream_user_open[phm->adapter_index][phm->
obj_index].open_flag = 0;
outstream_user_open[phm->adapter_index][phm->
obj_index].h_owner = NULL;
}
} else {
HPI_DEBUG_LOG(WARNING,
"%p trying to close %d outstream %d owned by %p\n",
h_owner, phm->adapter_index, phm->obj_index,
outstream_user_open[phm->adapter_index][phm->
obj_index].h_owner);
phr->error = HPI_ERROR_OBJ_NOT_OPEN;
}
hpios_msgxlock_un_lock(&msgx_lock);
}
static u16 adapter_prepare(u16 adapter)
{
struct hpi_message hm;
struct hpi_response hr;
/* Open the adapter and streams */
u16 i;
/* call to HPI_ADAPTER_OPEN */
hpi_init_message_response(&hm, &hr, HPI_OBJ_ADAPTER,
HPI_ADAPTER_OPEN);
hm.adapter_index = adapter;
hw_entry_point(&hm, &hr);
memcpy(&rESP_HPI_ADAPTER_OPEN[adapter], &hr,
sizeof(rESP_HPI_ADAPTER_OPEN[0]));
if (hr.error)
return hr.error;
/* call to HPI_ADAPTER_GET_INFO */
hpi_init_message_response(&hm, &hr, HPI_OBJ_ADAPTER,
HPI_ADAPTER_GET_INFO);
hm.adapter_index = adapter;
hw_entry_point(&hm, &hr);
if (hr.error)
return hr.error;
aDAPTER_INFO[adapter].num_outstreams = hr.u.a.num_outstreams;
aDAPTER_INFO[adapter].num_instreams = hr.u.a.num_instreams;
aDAPTER_INFO[adapter].type = hr.u.a.adapter_type;
gRESP_HPI_SUBSYS_FIND_ADAPTERS.s.aw_adapter_list[adapter] =
hr.u.a.adapter_type;
gRESP_HPI_SUBSYS_FIND_ADAPTERS.s.num_adapters++;
if (gRESP_HPI_SUBSYS_FIND_ADAPTERS.s.num_adapters > HPI_MAX_ADAPTERS)
gRESP_HPI_SUBSYS_FIND_ADAPTERS.s.num_adapters =
HPI_MAX_ADAPTERS;
/* call to HPI_OSTREAM_OPEN */
for (i = 0; i < aDAPTER_INFO[adapter].num_outstreams; i++) {
hpi_init_message_response(&hm, &hr, HPI_OBJ_OSTREAM,
HPI_OSTREAM_OPEN);
hm.adapter_index = adapter;
hm.obj_index = i;
hw_entry_point(&hm, &hr);
memcpy(&rESP_HPI_OSTREAM_OPEN[adapter][i], &hr,
sizeof(rESP_HPI_OSTREAM_OPEN[0][0]));
outstream_user_open[adapter][i].open_flag = 0;
outstream_user_open[adapter][i].h_owner = NULL;
}
/* call to HPI_ISTREAM_OPEN */
for (i = 0; i < aDAPTER_INFO[adapter].num_instreams; i++) {
hpi_init_message_response(&hm, &hr, HPI_OBJ_ISTREAM,
HPI_ISTREAM_OPEN);
hm.adapter_index = adapter;
hm.obj_index = i;
hw_entry_point(&hm, &hr);
memcpy(&rESP_HPI_ISTREAM_OPEN[adapter][i], &hr,
sizeof(rESP_HPI_ISTREAM_OPEN[0][0]));
instream_user_open[adapter][i].open_flag = 0;
instream_user_open[adapter][i].h_owner = NULL;
}
/* call to HPI_MIXER_OPEN */
hpi_init_message_response(&hm, &hr, HPI_OBJ_MIXER, HPI_MIXER_OPEN);
hm.adapter_index = adapter;
hw_entry_point(&hm, &hr);
memcpy(&rESP_HPI_MIXER_OPEN[adapter], &hr,
sizeof(rESP_HPI_MIXER_OPEN[0]));
return gRESP_HPI_SUBSYS_FIND_ADAPTERS.h.error;
}
static void HPIMSGX__reset(u16 adapter_index)
{
int i;
u16 adapter;
struct hpi_response hr;
if (adapter_index == HPIMSGX_ALLADAPTERS) {
/* reset all responses to contain errors */
hpi_init_response(&hr, HPI_OBJ_SUBSYSTEM,
HPI_SUBSYS_FIND_ADAPTERS, 0);
memcpy(&gRESP_HPI_SUBSYS_FIND_ADAPTERS, &hr,
sizeof(&gRESP_HPI_SUBSYS_FIND_ADAPTERS));
for (adapter = 0; adapter < HPI_MAX_ADAPTERS; adapter++) {
hpi_init_response(&hr, HPI_OBJ_ADAPTER,
HPI_ADAPTER_OPEN, HPI_ERROR_BAD_ADAPTER);
memcpy(&rESP_HPI_ADAPTER_OPEN[adapter], &hr,
sizeof(rESP_HPI_ADAPTER_OPEN[adapter]));
hpi_init_response(&hr, HPI_OBJ_MIXER, HPI_MIXER_OPEN,
HPI_ERROR_INVALID_OBJ);
memcpy(&rESP_HPI_MIXER_OPEN[adapter], &hr,
sizeof(rESP_HPI_MIXER_OPEN[adapter]));
for (i = 0; i < HPI_MAX_STREAMS; i++) {
hpi_init_response(&hr, HPI_OBJ_OSTREAM,
HPI_OSTREAM_OPEN,
HPI_ERROR_INVALID_OBJ);
memcpy(&rESP_HPI_OSTREAM_OPEN[adapter][i],
&hr,
sizeof(rESP_HPI_OSTREAM_OPEN[adapter]
[i]));
hpi_init_response(&hr, HPI_OBJ_ISTREAM,
HPI_ISTREAM_OPEN,
HPI_ERROR_INVALID_OBJ);
memcpy(&rESP_HPI_ISTREAM_OPEN[adapter][i],
&hr,
sizeof(rESP_HPI_ISTREAM_OPEN[adapter]
[i]));
}
}
} else if (adapter_index < HPI_MAX_ADAPTERS) {
rESP_HPI_ADAPTER_OPEN[adapter_index].h.error =
HPI_ERROR_BAD_ADAPTER;
rESP_HPI_MIXER_OPEN[adapter_index].h.error =
HPI_ERROR_INVALID_OBJ;
for (i = 0; i < HPI_MAX_STREAMS; i++) {
rESP_HPI_OSTREAM_OPEN[adapter_index][i].h.error =
HPI_ERROR_INVALID_OBJ;
rESP_HPI_ISTREAM_OPEN[adapter_index][i].h.error =
HPI_ERROR_INVALID_OBJ;
}
if (gRESP_HPI_SUBSYS_FIND_ADAPTERS.
s.aw_adapter_list[adapter_index]) {
gRESP_HPI_SUBSYS_FIND_ADAPTERS.
s.aw_adapter_list[adapter_index] = 0;
gRESP_HPI_SUBSYS_FIND_ADAPTERS.s.num_adapters--;
}
}
}
static u16 HPIMSGX__init(struct hpi_message *phm,
/* HPI_SUBSYS_CREATE_ADAPTER structure with */
/* resource list or NULL=find all */
struct hpi_response *phr
/* response from HPI_ADAPTER_GET_INFO */
)
{
hpi_handler_func *entry_point_func;
struct hpi_response hr;
if (gRESP_HPI_SUBSYS_FIND_ADAPTERS.s.num_adapters >= HPI_MAX_ADAPTERS)
return HPI_ERROR_BAD_ADAPTER_NUMBER;
/* Init response here so we can pass in previous adapter list */
hpi_init_response(&hr, phm->object, phm->function,
HPI_ERROR_INVALID_OBJ);
memcpy(hr.u.s.aw_adapter_list,
gRESP_HPI_SUBSYS_FIND_ADAPTERS.s.aw_adapter_list,
sizeof(gRESP_HPI_SUBSYS_FIND_ADAPTERS.s.aw_adapter_list));
entry_point_func =
hpi_lookup_entry_point_function(phm->u.s.resource.r.pci);
if (entry_point_func) {
HPI_DEBUG_MESSAGE(DEBUG, phm);
entry_point_func(phm, &hr);
} else {
phr->error = HPI_ERROR_PROCESSING_MESSAGE;
return phr->error;
}
if (hr.error == 0) {
/* the adapter was created succesfully
save the mapping for future use */
hpi_entry_points[hr.u.s.adapter_index] = entry_point_func;
/* prepare adapter (pre-open streams etc.) */
HPI_DEBUG_LOG(DEBUG,
"HPI_SUBSYS_CREATE_ADAPTER successful,"
" preparing adapter\n");
adapter_prepare(hr.u.s.adapter_index);
}
memcpy(phr, &hr, hr.size);
return phr->error;
}
static void HPIMSGX__cleanup(u16 adapter_index, void *h_owner)
{
int i, adapter, adapter_limit;
if (!h_owner)
return;
if (adapter_index == HPIMSGX_ALLADAPTERS) {
adapter = 0;
adapter_limit = HPI_MAX_ADAPTERS;
} else {
adapter = adapter_index;
adapter_limit = adapter + 1;
}
for (; adapter < adapter_limit; adapter++) {
/* printk(KERN_INFO "Cleanup adapter #%d\n",wAdapter); */
for (i = 0; i < HPI_MAX_STREAMS; i++) {
if (h_owner ==
outstream_user_open[adapter][i].h_owner) {
struct hpi_message hm;
struct hpi_response hr;
HPI_DEBUG_LOG(DEBUG,
"close adapter %d ostream %d\n",
adapter, i);
hpi_init_message_response(&hm, &hr,
HPI_OBJ_OSTREAM, HPI_OSTREAM_RESET);
hm.adapter_index = (u16)adapter;
hm.obj_index = (u16)i;
hw_entry_point(&hm, &hr);
hm.function = HPI_OSTREAM_HOSTBUFFER_FREE;
hw_entry_point(&hm, &hr);
hm.function = HPI_OSTREAM_GROUP_RESET;
hw_entry_point(&hm, &hr);
outstream_user_open[adapter][i].open_flag = 0;
outstream_user_open[adapter][i].h_owner =
NULL;
}
if (h_owner == instream_user_open[adapter][i].h_owner) {
struct hpi_message hm;
struct hpi_response hr;
HPI_DEBUG_LOG(DEBUG,
"close adapter %d istream %d\n",
adapter, i);
hpi_init_message_response(&hm, &hr,
HPI_OBJ_ISTREAM, HPI_ISTREAM_RESET);
hm.adapter_index = (u16)adapter;
hm.obj_index = (u16)i;
hw_entry_point(&hm, &hr);
hm.function = HPI_ISTREAM_HOSTBUFFER_FREE;
hw_entry_point(&hm, &hr);
hm.function = HPI_ISTREAM_GROUP_RESET;
hw_entry_point(&hm, &hr);
instream_user_open[adapter][i].open_flag = 0;
instream_user_open[adapter][i].h_owner = NULL;
}
}
}
}
/******************************************************************************
AudioScience HPI driver
Copyright (C) 1997-2010 AudioScience Inc. <support@audioscience.com>
This program is free software; you can redistribute it and/or modify
it under the terms of version 2 of the GNU General Public License as
published by the Free Software Foundation;
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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
HPI Extended Message Handler Functions
(C) Copyright AudioScience Inc. 1997-2003
******************************************************************************/
#ifndef _HPIMSGX_H_
#define _HPIMSGX_H_
#include "hpi_internal.h"
#define HPIMSGX_ALLADAPTERS (0xFFFF)
void hpi_send_recv_ex(struct hpi_message *phm, struct hpi_response *phr,
void *h_owner);
#define HPI_MESSAGE_LOWER_LAYER hpi_send_recv_ex
#endif /* _HPIMSGX_H_ */
/*******************************************************************************
AudioScience HPI driver
Copyright (C) 1997-2010 AudioScience Inc. <support@audioscience.com>
This program is free software; you can redistribute it and/or modify
it under the terms of version 2 of the GNU General Public License as
published by the Free Software Foundation;
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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
Common Linux HPI ioctl and module probe/remove functions
*******************************************************************************/
#define SOURCEFILE_NAME "hpioctl.c"
#include "hpi_internal.h"
#include "hpimsginit.h"
#include "hpidebug.h"
#include "hpimsgx.h"
#include "hpioctl.h"
#include <linux/fs.h>
#include <linux/slab.h>
#include <linux/moduleparam.h>
#include <asm/uaccess.h>
#include <linux/stringify.h>
#ifdef MODULE_FIRMWARE
MODULE_FIRMWARE("asihpi/dsp5000.bin");
MODULE_FIRMWARE("asihpi/dsp6200.bin");
MODULE_FIRMWARE("asihpi/dsp6205.bin");
MODULE_FIRMWARE("asihpi/dsp6400.bin");
MODULE_FIRMWARE("asihpi/dsp6600.bin");
MODULE_FIRMWARE("asihpi/dsp8700.bin");
MODULE_FIRMWARE("asihpi/dsp8900.bin");
#endif
static int prealloc_stream_buf;
module_param(prealloc_stream_buf, int, S_IRUGO);
MODULE_PARM_DESC(prealloc_stream_buf,
"preallocate size for per-adapter stream buffer");
/* Allow the debug level to be changed after module load.
E.g. echo 2 > /sys/module/asihpi/parameters/hpiDebugLevel
*/
module_param(hpi_debug_level, int, S_IRUGO | S_IWUSR);
MODULE_PARM_DESC(hpi_debug_level, "debug verbosity 0..5");
/* List of adapters found */
static struct hpi_adapter adapters[HPI_MAX_ADAPTERS];
/* Wrapper function to HPI_Message to enable dumping of the
message and response types.
*/
static void hpi_send_recv_f(struct hpi_message *phm, struct hpi_response *phr,
struct file *file)
{
int adapter = phm->adapter_index;
if ((adapter >= HPI_MAX_ADAPTERS || adapter < 0)
&& (phm->object != HPI_OBJ_SUBSYSTEM))
phr->error = HPI_ERROR_INVALID_OBJ_INDEX;
else
hpi_send_recv_ex(phm, phr, file);
}
/* This is called from hpifunc.c functions, called by ALSA
* (or other kernel process) In this case there is no file descriptor
* available for the message cache code
*/
void hpi_send_recv(struct hpi_message *phm, struct hpi_response *phr)
{
hpi_send_recv_f(phm, phr, HOWNER_KERNEL);
}
EXPORT_SYMBOL(hpi_send_recv);
/* for radio-asihpi */
int asihpi_hpi_release(struct file *file)
{
struct hpi_message hm;
struct hpi_response hr;
/* HPI_DEBUG_LOG(INFO,"hpi_release file %p, pid %d\n", file, current->pid); */
/* close the subsystem just in case the application forgot to. */
hpi_init_message_response(&hm, &hr, HPI_OBJ_SUBSYSTEM,
HPI_SUBSYS_CLOSE);
hpi_send_recv_ex(&hm, &hr, file);
return 0;
}
long asihpi_hpi_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
{
struct hpi_ioctl_linux __user *phpi_ioctl_data;
void __user *puhm;
void __user *puhr;
union hpi_message_buffer_v1 *hm;
union hpi_response_buffer_v1 *hr;
u16 res_max_size;
u32 uncopied_bytes;
struct hpi_adapter *pa = NULL;
int err = 0;
if (cmd != HPI_IOCTL_LINUX)
return -EINVAL;
hm = kmalloc(sizeof(*hm), GFP_KERNEL);
hr = kmalloc(sizeof(*hr), GFP_KERNEL);
if (!hm || !hr) {
err = -ENOMEM;
goto out;
}
phpi_ioctl_data = (struct hpi_ioctl_linux __user *)arg;
/* Read the message and response pointers from user space. */
get_user(puhm, &phpi_ioctl_data->phm);
get_user(puhr, &phpi_ioctl_data->phr);
/* Now read the message size and data from user space. */
get_user(hm->h.size, (u16 __user *)puhm);
if (hm->h.size > sizeof(*hm))
hm->h.size = sizeof(*hm);
/*printk(KERN_INFO "message size %d\n", hm->h.wSize); */
uncopied_bytes = copy_from_user(hm, puhm, hm->h.size);
if (uncopied_bytes) {
HPI_DEBUG_LOG(ERROR, "uncopied bytes %d\n", uncopied_bytes);
err = -EFAULT;
goto out;
}
get_user(res_max_size, (u16 __user *)puhr);
/* printk(KERN_INFO "user response size %d\n", res_max_size); */
if (res_max_size < sizeof(struct hpi_response_header)) {
HPI_DEBUG_LOG(WARNING, "small res size %d\n", res_max_size);
err = -EFAULT;
goto out;
}
pa = &adapters[hm->h.adapter_index];
hr->h.size = 0;
if (hm->h.object == HPI_OBJ_SUBSYSTEM) {
switch (hm->h.function) {
case HPI_SUBSYS_CREATE_ADAPTER:
case HPI_SUBSYS_DELETE_ADAPTER:
/* Application must not use these functions! */
hr->h.size = sizeof(hr->h);
hr->h.error = HPI_ERROR_INVALID_OPERATION;
hr->h.function = hm->h.function;
uncopied_bytes = copy_to_user(puhr, hr, hr->h.size);
if (uncopied_bytes)
err = -EFAULT;
else
err = 0;
goto out;
default:
hpi_send_recv_f(&hm->m0, &hr->r0, file);
}
} else {
u16 __user *ptr = NULL;
u32 size = 0;
/* -1=no data 0=read from user mem, 1=write to user mem */
int wrflag = -1;
u32 adapter = hm->h.adapter_index;
if ((hm->h.adapter_index > HPI_MAX_ADAPTERS) || (!pa->type)) {
hpi_init_response(&hr->r0, HPI_OBJ_ADAPTER,
HPI_ADAPTER_OPEN,
HPI_ERROR_BAD_ADAPTER_NUMBER);
uncopied_bytes =
copy_to_user(puhr, hr, sizeof(hr->h));
if (uncopied_bytes)
err = -EFAULT;
else
err = 0;
goto out;
}
if (mutex_lock_interruptible(&adapters[adapter].mutex)) {
err = -EINTR;
goto out;
}
/* Dig out any pointers embedded in the message. */
switch (hm->h.function) {
case HPI_OSTREAM_WRITE:
case HPI_ISTREAM_READ:{
/* Yes, sparse, this is correct. */
ptr = (u16 __user *)hm->m0.u.d.u.data.pb_data;
size = hm->m0.u.d.u.data.data_size;
/* Allocate buffer according to application request.
?Is it better to alloc/free for the duration
of the transaction?
*/
if (pa->buffer_size < size) {
HPI_DEBUG_LOG(DEBUG,
"realloc adapter %d stream "
"buffer from %zd to %d\n",
hm->h.adapter_index,
pa->buffer_size, size);
if (pa->p_buffer) {
pa->buffer_size = 0;
vfree(pa->p_buffer);
}
pa->p_buffer = vmalloc(size);
if (pa->p_buffer)
pa->buffer_size = size;
else {
HPI_DEBUG_LOG(ERROR,
"HPI could not allocate "
"stream buffer size %d\n",
size);
mutex_unlock(&adapters
[adapter].mutex);
err = -EINVAL;
goto out;
}
}
hm->m0.u.d.u.data.pb_data = pa->p_buffer;
if (hm->h.function == HPI_ISTREAM_READ)
/* from card, WRITE to user mem */
wrflag = 1;
else
wrflag = 0;
break;
}
default:
size = 0;
break;
}
if (size && (wrflag == 0)) {
uncopied_bytes =
copy_from_user(pa->p_buffer, ptr, size);
if (uncopied_bytes)
HPI_DEBUG_LOG(WARNING,
"missed %d of %d "
"bytes from user\n", uncopied_bytes,
size);
}
hpi_send_recv_f(&hm->m0, &hr->r0, file);
if (size && (wrflag == 1)) {
uncopied_bytes =
copy_to_user(ptr, pa->p_buffer, size);
if (uncopied_bytes)
HPI_DEBUG_LOG(WARNING,
"missed %d of %d " "bytes to user\n",
uncopied_bytes, size);
}
mutex_unlock(&adapters[adapter].mutex);
}
/* on return response size must be set */
/*printk(KERN_INFO "response size %d\n", hr->h.wSize); */
if (!hr->h.size) {
HPI_DEBUG_LOG(ERROR, "response zero size\n");
err = -EFAULT;
goto out;
}
if (hr->h.size > res_max_size) {
HPI_DEBUG_LOG(ERROR, "response too big %d %d\n", hr->h.size,
res_max_size);
/*HPI_DEBUG_MESSAGE(ERROR, hm); */
err = -EFAULT;
goto out;
}
uncopied_bytes = copy_to_user(puhr, hr, hr->h.size);
if (uncopied_bytes) {
HPI_DEBUG_LOG(ERROR, "uncopied bytes %d\n", uncopied_bytes);
err = -EFAULT;
goto out;
}
out:
kfree(hm);
kfree(hr);
return err;
}
int __devinit asihpi_adapter_probe(struct pci_dev *pci_dev,
const struct pci_device_id *pci_id)
{
int err, idx, nm;
unsigned int memlen;
struct hpi_message hm;
struct hpi_response hr;
struct hpi_adapter adapter;
struct hpi_pci pci;
memset(&adapter, 0, sizeof(adapter));
printk(KERN_DEBUG "probe PCI device (%04x:%04x,%04x:%04x,%04x)\n",
pci_dev->vendor, pci_dev->device, pci_dev->subsystem_vendor,
pci_dev->subsystem_device, pci_dev->devfn);
hpi_init_message_response(&hm, &hr, HPI_OBJ_SUBSYSTEM,
HPI_SUBSYS_CREATE_ADAPTER);
hpi_init_response(&hr, HPI_OBJ_SUBSYSTEM, HPI_SUBSYS_CREATE_ADAPTER,
HPI_ERROR_PROCESSING_MESSAGE);
hm.adapter_index = -1; /* an invalid index */
/* fill in HPI_PCI information from kernel provided information */
adapter.pci = pci_dev;
nm = HPI_MAX_ADAPTER_MEM_SPACES;
for (idx = 0; idx < nm; idx++) {
HPI_DEBUG_LOG(INFO, "resource %d %s %08llx-%08llx %04llx\n",
idx, pci_dev->resource[idx].name,
(unsigned long long)pci_resource_start(pci_dev, idx),
(unsigned long long)pci_resource_end(pci_dev, idx),
(unsigned long long)pci_resource_flags(pci_dev, idx));
if (pci_resource_flags(pci_dev, idx) & IORESOURCE_MEM) {
memlen = pci_resource_len(pci_dev, idx);
adapter.ap_remapped_mem_base[idx] =
ioremap(pci_resource_start(pci_dev, idx),
memlen);
if (!adapter.ap_remapped_mem_base[idx]) {
HPI_DEBUG_LOG(ERROR,
"ioremap failed, aborting\n");
/* unmap previously mapped pci mem space */
goto err;
}
}
pci.ap_mem_base[idx] = adapter.ap_remapped_mem_base[idx];
}
/* could replace Pci with direct pointer to pci_dev for linux
Instead wrap accessor functions for IDs etc.
Would it work for windows?
*/
pci.bus_number = pci_dev->bus->number;
pci.vendor_id = (u16)pci_dev->vendor;
pci.device_id = (u16)pci_dev->device;
pci.subsys_vendor_id = (u16)(pci_dev->subsystem_vendor & 0xffff);
pci.subsys_device_id = (u16)(pci_dev->subsystem_device & 0xffff);
pci.device_number = pci_dev->devfn;
pci.interrupt = pci_dev->irq;
pci.p_os_data = pci_dev;
hm.u.s.resource.bus_type = HPI_BUS_PCI;
hm.u.s.resource.r.pci = &pci;
/* call CreateAdapterObject on the relevant hpi module */
hpi_send_recv_ex(&hm, &hr, HOWNER_KERNEL);
if (hr.error)
goto err;
if (prealloc_stream_buf) {
adapter.p_buffer = vmalloc(prealloc_stream_buf);
if (!adapter.p_buffer) {
HPI_DEBUG_LOG(ERROR,
"HPI could not allocate "
"kernel buffer size %d\n",
prealloc_stream_buf);
goto err;
}
}
adapter.index = hr.u.s.adapter_index;
adapter.type = hr.u.s.aw_adapter_list[adapter.index];
hm.adapter_index = adapter.index;
err = hpi_adapter_open(NULL, adapter.index);
if (err)
goto err;
adapter.snd_card_asihpi = NULL;
/* WARNING can't init mutex in 'adapter'
* and then copy it to adapters[] ?!?!
*/
adapters[hr.u.s.adapter_index] = adapter;
mutex_init(&adapters[adapter.index].mutex);
pci_set_drvdata(pci_dev, &adapters[adapter.index]);
printk(KERN_INFO "probe found adapter ASI%04X HPI index #%d.\n",
adapter.type, adapter.index);
return 0;
err:
for (idx = 0; idx < HPI_MAX_ADAPTER_MEM_SPACES; idx++) {
if (adapter.ap_remapped_mem_base[idx]) {
iounmap(adapter.ap_remapped_mem_base[idx]);
adapter.ap_remapped_mem_base[idx] = NULL;
}
}
if (adapter.p_buffer) {
adapter.buffer_size = 0;
vfree(adapter.p_buffer);
}
HPI_DEBUG_LOG(ERROR, "adapter_probe failed\n");
return -ENODEV;
}
void __devexit asihpi_adapter_remove(struct pci_dev *pci_dev)
{
int idx;
struct hpi_message hm;
struct hpi_response hr;
struct hpi_adapter *pa;
pa = (struct hpi_adapter *)pci_get_drvdata(pci_dev);
hpi_init_message_response(&hm, &hr, HPI_OBJ_SUBSYSTEM,
HPI_SUBSYS_DELETE_ADAPTER);
hm.adapter_index = pa->index;
hpi_send_recv_ex(&hm, &hr, HOWNER_KERNEL);
/* unmap PCI memory space, mapped during device init. */
for (idx = 0; idx < HPI_MAX_ADAPTER_MEM_SPACES; idx++) {
if (pa->ap_remapped_mem_base[idx]) {
iounmap(pa->ap_remapped_mem_base[idx]);
pa->ap_remapped_mem_base[idx] = NULL;
}
}
if (pa->p_buffer) {
pa->buffer_size = 0;
vfree(pa->p_buffer);
}
pci_set_drvdata(pci_dev, NULL);
/*
printk(KERN_INFO "PCI device (%04x:%04x,%04x:%04x,%04x),"
" HPI index # %d, removed.\n",
pci_dev->vendor, pci_dev->device,
pci_dev->subsystem_vendor,
pci_dev->subsystem_device, pci_dev->devfn,
pa->index);
*/
}
void __init asihpi_init(void)
{
struct hpi_message hm;
struct hpi_response hr;
memset(adapters, 0, sizeof(adapters));
printk(KERN_INFO "ASIHPI driver %d.%02d.%02d\n",
HPI_VER_MAJOR(HPI_VER), HPI_VER_MINOR(HPI_VER),
HPI_VER_RELEASE(HPI_VER));
hpi_init_message_response(&hm, &hr, HPI_OBJ_SUBSYSTEM,
HPI_SUBSYS_DRIVER_LOAD);
hpi_send_recv_ex(&hm, &hr, HOWNER_KERNEL);
}
void asihpi_exit(void)
{
struct hpi_message hm;
struct hpi_response hr;
hpi_init_message_response(&hm, &hr, HPI_OBJ_SUBSYSTEM,
HPI_SUBSYS_DRIVER_UNLOAD);
hpi_send_recv_ex(&hm, &hr, HOWNER_KERNEL);
}
/*******************************************************************************
AudioScience HPI driver
Copyright (C) 1997-2010 AudioScience Inc. <support@audioscience.com>
This program is free software; you can redistribute it and/or modify
it under the terms of version 2 of the GNU General Public License as
published by the Free Software Foundation;
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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
Linux HPI ioctl, and shared module init functions
*******************************************************************************/
int __devinit asihpi_adapter_probe(struct pci_dev *pci_dev,
const struct pci_device_id *pci_id);
void __devexit asihpi_adapter_remove(struct pci_dev *pci_dev);
void __init asihpi_init(void);
void __exit asihpi_exit(void);
int asihpi_hpi_release(struct file *file);
long asihpi_hpi_ioctl(struct file *file, unsigned int cmd, unsigned long arg);
/* This is called from hpifunc.c functions, called by ALSA
* (or other kernel process) In this case there is no file descriptor
* available for the message cache code
*/
void hpi_send_recv(struct hpi_message *phm, struct hpi_response *phr);
#define HOWNER_KERNEL ((void *)-1)
/******************************************************************************
AudioScience HPI driver
Copyright (C) 1997-2010 AudioScience Inc. <support@audioscience.com>
This program is free software; you can redistribute it and/or modify
it under the terms of version 2 of the GNU General Public License as
published by the Free Software Foundation;
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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
HPI Operating System function implementation for Linux
(C) Copyright AudioScience Inc. 1997-2003
******************************************************************************/
#define SOURCEFILE_NAME "hpios.c"
#include "hpi_internal.h"
#include "hpidebug.h"
#include <linux/delay.h>
#include <linux/sched.h>
void hpios_delay_micro_seconds(u32 num_micro_sec)
{
if ((usecs_to_jiffies(num_micro_sec) > 1) && !in_interrupt()) {
/* MUST NOT SCHEDULE IN INTERRUPT CONTEXT! */
schedule_timeout_uninterruptible(usecs_to_jiffies
(num_micro_sec));
} else if (num_micro_sec <= 2000)
udelay(num_micro_sec);
else
mdelay(num_micro_sec / 1000);
}
void hpios_locked_mem_init(void)
{
}
/** Allocated an area of locked memory for bus master DMA operations.
On error, return -ENOMEM, and *pMemArea.size = 0
*/
u16 hpios_locked_mem_alloc(struct consistent_dma_area *p_mem_area, u32 size,
struct pci_dev *pdev)
{
/*?? any benefit in using managed dmam_alloc_coherent? */
p_mem_area->vaddr =
dma_alloc_coherent(&pdev->dev, size, &p_mem_area->dma_handle,
GFP_DMA32 | GFP_KERNEL);
if (p_mem_area->vaddr) {
HPI_DEBUG_LOG(DEBUG, "allocated %d bytes, dma 0x%x vma %p\n",
size, (unsigned int)p_mem_area->dma_handle,
p_mem_area->vaddr);
p_mem_area->pdev = &pdev->dev;
p_mem_area->size = size;
return 0;
} else {
HPI_DEBUG_LOG(WARNING,
"failed to allocate %d bytes locked memory\n", size);
p_mem_area->size = 0;
return -ENOMEM;
}
}
u16 hpios_locked_mem_free(struct consistent_dma_area *p_mem_area)
{
if (p_mem_area->size) {
dma_free_coherent(p_mem_area->pdev, p_mem_area->size,
p_mem_area->vaddr, p_mem_area->dma_handle);
HPI_DEBUG_LOG(DEBUG, "freed %lu bytes, dma 0x%x vma %p\n",
(unsigned long)p_mem_area->size,
(unsigned int)p_mem_area->dma_handle,
p_mem_area->vaddr);
p_mem_area->size = 0;
return 0;
} else {
return 1;
}
}
void hpios_locked_mem_free_all(void)
{
}
void __iomem *hpios_map_io(struct pci_dev *pci_dev, int idx,
unsigned int length)
{
HPI_DEBUG_LOG(DEBUG, "mapping %d %s %08llx-%08llx %04llx len 0x%x\n",
idx, pci_dev->resource[idx].name,
(unsigned long long)pci_resource_start(pci_dev, idx),
(unsigned long long)pci_resource_end(pci_dev, idx),
(unsigned long long)pci_resource_flags(pci_dev, idx), length);
if (!(pci_resource_flags(pci_dev, idx) & IORESOURCE_MEM)) {
HPI_DEBUG_LOG(ERROR, "not an io memory resource\n");
return NULL;
}
if (length > pci_resource_len(pci_dev, idx)) {
HPI_DEBUG_LOG(ERROR, "resource too small for requested %d \n",
length);
return NULL;
}
return ioremap(pci_resource_start(pci_dev, idx), length);
}
/******************************************************************************
AudioScience HPI driver
Copyright (C) 1997-2010 AudioScience Inc. <support@audioscience.com>
This program is free software; you can redistribute it and/or modify
it under the terms of version 2 of the GNU General Public License as
published by the Free Software Foundation;
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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
HPI Operating System Specific macros for Linux Kernel driver
(C) Copyright AudioScience Inc. 1997-2003
******************************************************************************/
#ifndef _HPIOS_H_
#define _HPIOS_H_
#undef HPI_OS_LINUX_KERNEL
#define HPI_OS_LINUX_KERNEL
#define HPI_OS_DEFINED
#define HPI_KERNEL_MODE
#define HPI_REASSIGN_DUPLICATE_ADAPTER_IDX
#include <linux/io.h>
#include <asm/system.h>
#include <linux/ioctl.h>
#include <linux/kernel.h>
#include <linux/string.h>
#include <linux/device.h>
#include <linux/firmware.h>
#include <linux/interrupt.h>
#include <linux/pci.h>
#define HPI_NO_OS_FILE_OPS
#ifdef CONFIG_64BIT
#define HPI64BIT
#endif
/** Details of a memory area allocated with pci_alloc_consistent
Need all info for parameters to pci_free_consistent
*/
struct consistent_dma_area {
struct device *pdev;
/* looks like dma-mapping dma_devres ?! */
size_t size;
void *vaddr;
dma_addr_t dma_handle;
};
static inline u16 hpios_locked_mem_get_phys_addr(struct consistent_dma_area
*locked_mem_handle, u32 *p_physical_addr)
{
*p_physical_addr = locked_mem_handle->dma_handle;
return 0;
}
static inline u16 hpios_locked_mem_get_virt_addr(struct consistent_dma_area
*locked_mem_handle, void **pp_virtual_addr)
{
*pp_virtual_addr = locked_mem_handle->vaddr;
return 0;
}
static inline u16 hpios_locked_mem_valid(struct consistent_dma_area
*locked_mem_handle)
{
return locked_mem_handle->size != 0;
}
struct hpi_ioctl_linux {
void __user *phm;
void __user *phr;
};
/* Conflict?: H is already used by a number of drivers hid, bluetooth hci,
and some sound drivers sb16, hdsp, emu10k. AFAIK 0xFC is ununsed command
*/
#define HPI_IOCTL_LINUX _IOWR('H', 0xFC, struct hpi_ioctl_linux)
#define HPI_DEBUG_FLAG_ERROR KERN_ERR
#define HPI_DEBUG_FLAG_WARNING KERN_WARNING
#define HPI_DEBUG_FLAG_NOTICE KERN_NOTICE
#define HPI_DEBUG_FLAG_INFO KERN_INFO
#define HPI_DEBUG_FLAG_DEBUG KERN_DEBUG
#define HPI_DEBUG_FLAG_VERBOSE KERN_DEBUG /* kernel has no verbose */
#include <linux/spinlock.h>
#define HPI_LOCKING
struct hpios_spinlock {
spinlock_t lock; /* SEE hpios_spinlock */
int lock_context;
};
/* The reason for all this evilness is that ALSA calls some of a drivers
* operators in atomic context, and some not. But all our functions channel
* through the HPI_Message conduit, so we can't handle the different context
* per function
*/
#define IN_LOCK_BH 1
#define IN_LOCK_IRQ 0
static inline void cond_lock(struct hpios_spinlock *l)
{
if (irqs_disabled()) {
/* NO bh or isr can execute on this processor,
so ordinary lock will do
*/
spin_lock(&((l)->lock));
l->lock_context = IN_LOCK_IRQ;
} else {
spin_lock_bh(&((l)->lock));
l->lock_context = IN_LOCK_BH;
}
}
static inline void cond_unlock(struct hpios_spinlock *l)
{
if (l->lock_context == IN_LOCK_BH)
spin_unlock_bh(&((l)->lock));
else
spin_unlock(&((l)->lock));
}
#define hpios_msgxlock_init(obj) spin_lock_init(&(obj)->lock)
#define hpios_msgxlock_lock(obj) cond_lock(obj)
#define hpios_msgxlock_un_lock(obj) cond_unlock(obj)
#define hpios_dsplock_init(obj) spin_lock_init(&(obj)->dsp_lock.lock)
#define hpios_dsplock_lock(obj) cond_lock(&(obj)->dsp_lock)
#define hpios_dsplock_unlock(obj) cond_unlock(&(obj)->dsp_lock)
#ifdef CONFIG_SND_DEBUG
#define HPI_DEBUG
#endif
#define HPI_ALIST_LOCKING
#define hpios_alistlock_init(obj) spin_lock_init(&((obj)->list_lock.lock))
#define hpios_alistlock_lock(obj) spin_lock(&((obj)->list_lock.lock))
#define hpios_alistlock_un_lock(obj) spin_unlock(&((obj)->list_lock.lock))
struct hpi_adapter {
/* mutex prevents contention for one card
between multiple user programs (via ioctl) */
struct mutex mutex;
u16 index;
u16 type;
/* ALSA card structure */
void *snd_card_asihpi;
char *p_buffer;
size_t buffer_size;
struct pci_dev *pci;
void __iomem *ap_remapped_mem_base[HPI_MAX_ADAPTER_MEM_SPACES];
};
static inline void hpios_unmap_io(void __iomem *addr,
unsigned long size)
{
iounmap(addr);
}
void __iomem *hpios_map_io(struct pci_dev *pci_dev, int idx,
unsigned int length);
#endif
/******************************************************************************
AudioScience HPI driver
Copyright (C) 1997-2010 AudioScience Inc. <support@audioscience.com>
This program is free software; you can redistribute it and/or modify
it under the terms of version 2 of the GNU General Public License as
published by the Free Software Foundation;
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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
Array initializer for PCI card IDs
(C) Copyright AudioScience Inc. 1998-2003
*******************************************************************************/
/*NOTE: when adding new lines to this header file
they MUST be grouped by HPI entry point.
*/
{
HPI_PCI_VENDOR_ID_TI, HPI_PCI_DEV_ID_DSP6205,
HPI_PCI_VENDOR_ID_AUDIOSCIENCE, PCI_ANY_ID, 0, 0,
(kernel_ulong_t) HPI_6205}
, {
HPI_PCI_VENDOR_ID_TI, HPI_PCI_DEV_ID_PCI2040,
HPI_PCI_VENDOR_ID_AUDIOSCIENCE, PCI_ANY_ID, 0, 0,
(kernel_ulong_t) HPI_6000}
, {
0}
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment