ALSA: asihpi: Add support for stream interrupt.
Some cards have a so-called low-latency mode, in which they present a single multichannel stream with no mixing or samplerate conversion. In this mode the card can generate an interrupt per internal processing block (typically 32 or 64 frames) Signed-off-by: Eliot Blennerhassett <eliot@blennerhassett.gen.nz> Signed-off-by: Takashi Iwai <tiwai@suse.de>
This commit is contained in:
parent
c1464a8854
commit
f9a376c3f6
|
@ -1,6 +1,6 @@
|
|||
/*
|
||||
* Asihpi soundcard
|
||||
* Copyright (c) by AudioScience Inc <alsa@audioscience.com>
|
||||
* Copyright (c) by 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
|
||||
|
@ -124,6 +124,16 @@ struct snd_card_asihpi {
|
|||
struct pci_dev *pci;
|
||||
struct hpi_adapter *hpi;
|
||||
|
||||
/* In low latency mode there is only one stream, a pointer to its
|
||||
* private data is stored here on trigger and cleared on stop.
|
||||
* The interrupt handler uses it as a parameter when calling
|
||||
* snd_card_asihpi_timer_function().
|
||||
*/
|
||||
struct snd_card_asihpi_pcm *llmode_streampriv;
|
||||
struct tasklet_struct t;
|
||||
void (*pcm_start)(struct snd_pcm_substream *substream);
|
||||
void (*pcm_stop)(struct snd_pcm_substream *substream);
|
||||
|
||||
u32 h_mixer;
|
||||
struct clk_cache cc;
|
||||
|
||||
|
@ -544,6 +554,48 @@ static void snd_card_asihpi_pcm_timer_stop(struct snd_pcm_substream *substream)
|
|||
del_timer(&dpcm->timer);
|
||||
}
|
||||
|
||||
static void snd_card_asihpi_pcm_int_start(struct snd_pcm_substream *substream)
|
||||
{
|
||||
struct snd_card_asihpi_pcm *dpcm;
|
||||
struct snd_card_asihpi *card;
|
||||
|
||||
BUG_ON(!substream);
|
||||
|
||||
dpcm = (struct snd_card_asihpi_pcm *)substream->runtime->private_data;
|
||||
card = snd_pcm_substream_chip(substream);
|
||||
|
||||
BUG_ON(in_interrupt());
|
||||
tasklet_disable(&card->t);
|
||||
card->llmode_streampriv = dpcm;
|
||||
tasklet_enable(&card->t);
|
||||
|
||||
hpi_handle_error(hpi_adapter_set_property(card->hpi->adapter->index,
|
||||
HPI_ADAPTER_PROPERTY_IRQ_RATE,
|
||||
card->update_interval_frames, 0));
|
||||
}
|
||||
|
||||
static void snd_card_asihpi_pcm_int_stop(struct snd_pcm_substream *substream)
|
||||
{
|
||||
struct snd_card_asihpi_pcm *dpcm;
|
||||
struct snd_card_asihpi *card;
|
||||
|
||||
BUG_ON(!substream);
|
||||
|
||||
dpcm = (struct snd_card_asihpi_pcm *)substream->runtime->private_data;
|
||||
card = snd_pcm_substream_chip(substream);
|
||||
|
||||
hpi_handle_error(hpi_adapter_set_property(card->hpi->adapter->index,
|
||||
HPI_ADAPTER_PROPERTY_IRQ_RATE, 0, 0));
|
||||
|
||||
if (in_interrupt())
|
||||
card->llmode_streampriv = NULL;
|
||||
else {
|
||||
tasklet_disable(&card->t);
|
||||
card->llmode_streampriv = NULL;
|
||||
tasklet_enable(&card->t);
|
||||
}
|
||||
}
|
||||
|
||||
static int snd_card_asihpi_trigger(struct snd_pcm_substream *substream,
|
||||
int cmd)
|
||||
{
|
||||
|
@ -602,7 +654,7 @@ static int snd_card_asihpi_trigger(struct snd_pcm_substream *substream,
|
|||
break;
|
||||
}
|
||||
/* start the master stream */
|
||||
snd_card_asihpi_pcm_timer_start(substream);
|
||||
card->pcm_start(substream);
|
||||
if ((substream->stream == SNDRV_PCM_STREAM_CAPTURE) ||
|
||||
!card->can_dma)
|
||||
hpi_handle_error(hpi_stream_start(dpcm->h_stream));
|
||||
|
@ -610,7 +662,7 @@ static int snd_card_asihpi_trigger(struct snd_pcm_substream *substream,
|
|||
|
||||
case SNDRV_PCM_TRIGGER_STOP:
|
||||
snd_printdd("%s trigger stop\n", name);
|
||||
snd_card_asihpi_pcm_timer_stop(substream);
|
||||
card->pcm_stop(substream);
|
||||
snd_pcm_group_for_each_entry(s, substream) {
|
||||
if (snd_pcm_substream_chip(s) != card)
|
||||
continue;
|
||||
|
@ -641,12 +693,12 @@ static int snd_card_asihpi_trigger(struct snd_pcm_substream *substream,
|
|||
|
||||
case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
|
||||
snd_printdd("%s trigger pause release\n", name);
|
||||
card->pcm_start(substream);
|
||||
hpi_handle_error(hpi_stream_start(dpcm->h_stream));
|
||||
snd_card_asihpi_pcm_timer_start(substream);
|
||||
break;
|
||||
case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
|
||||
snd_printdd("%s trigger pause push\n", name);
|
||||
snd_card_asihpi_pcm_timer_stop(substream);
|
||||
card->pcm_stop(substream);
|
||||
hpi_handle_error(hpi_stream_stop(dpcm->h_stream));
|
||||
break;
|
||||
default:
|
||||
|
@ -718,8 +770,8 @@ static void snd_card_asihpi_timer_function(unsigned long data)
|
|||
u32 buffer_size, bytes_avail, samples_played, on_card_bytes;
|
||||
char name[16];
|
||||
|
||||
snd_pcm_debug_name(substream, name, sizeof(name));
|
||||
|
||||
snd_pcm_debug_name(substream, name, sizeof(name));
|
||||
|
||||
/* find minimum newdata and buffer pos in group */
|
||||
snd_pcm_group_for_each_entry(s, substream) {
|
||||
|
@ -779,7 +831,8 @@ static void snd_card_asihpi_timer_function(unsigned long data)
|
|||
newdata);
|
||||
}
|
||||
|
||||
snd_printddd("timer1, %s, %d, S=%d, elap=%d, rw=%d, dsp=%d, left=%d, aux=%d, space=%d, hw_ptr=%ld, appl_ptr=%ld\n",
|
||||
snd_printddd(
|
||||
"timer1, %s, %d, S=%d, elap=%d, rw=%d, dsp=%d, left=%d, aux=%d, space=%d, hw_ptr=%ld, appl_ptr=%ld\n",
|
||||
name, s->number, state,
|
||||
ds->pcm_buf_elapsed_dma_ofs,
|
||||
ds->pcm_buf_host_rw_ofs,
|
||||
|
@ -815,11 +868,13 @@ static void snd_card_asihpi_timer_function(unsigned long data)
|
|||
|
||||
snd_pcm_group_for_each_entry(s, substream) {
|
||||
struct snd_card_asihpi_pcm *ds = s->runtime->private_data;
|
||||
runtime = s->runtime;
|
||||
|
||||
/* don't link Cap and Play */
|
||||
if (substream->stream != s->stream)
|
||||
continue;
|
||||
|
||||
/* Store dma offset for use by pointer callback */
|
||||
ds->pcm_buf_dma_ofs = pcm_buf_dma_ofs;
|
||||
|
||||
if (xfercount &&
|
||||
|
@ -878,16 +933,38 @@ static void snd_card_asihpi_timer_function(unsigned long data)
|
|||
pd, xfer2));
|
||||
}
|
||||
}
|
||||
/* ? host_rw_ofs always ahead of elapsed_dma_ofs by preload size? */
|
||||
ds->pcm_buf_host_rw_ofs += xfercount;
|
||||
ds->pcm_buf_elapsed_dma_ofs += xfercount;
|
||||
snd_pcm_period_elapsed(s);
|
||||
}
|
||||
}
|
||||
|
||||
if (dpcm->respawn_timer)
|
||||
if (!card->hpi->interrupt_mode && dpcm->respawn_timer)
|
||||
add_timer(&dpcm->timer);
|
||||
}
|
||||
|
||||
static void snd_card_asihpi_int_task(unsigned long data)
|
||||
{
|
||||
struct hpi_adapter *a = (struct hpi_adapter *)data;
|
||||
struct snd_card_asihpi *asihpi;
|
||||
|
||||
WARN_ON(!a || !a->snd_card || !a->snd_card->private_data);
|
||||
asihpi = (struct snd_card_asihpi *)a->snd_card->private_data;
|
||||
if (asihpi->llmode_streampriv)
|
||||
snd_card_asihpi_timer_function(
|
||||
(unsigned long)asihpi->llmode_streampriv);
|
||||
}
|
||||
|
||||
static void snd_card_asihpi_isr(struct hpi_adapter *a)
|
||||
{
|
||||
struct snd_card_asihpi *asihpi;
|
||||
|
||||
WARN_ON(!a || !a->snd_card || !a->snd_card->private_data);
|
||||
asihpi = (struct snd_card_asihpi *)a->snd_card->private_data;
|
||||
tasklet_schedule(&asihpi->t);
|
||||
}
|
||||
|
||||
/***************************** PLAYBACK OPS ****************/
|
||||
static int snd_card_asihpi_playback_ioctl(struct snd_pcm_substream *substream,
|
||||
unsigned int cmd, void *arg)
|
||||
|
@ -995,13 +1072,22 @@ static int snd_card_asihpi_playback_open(struct snd_pcm_substream *substream)
|
|||
runtime->private_free = snd_card_asihpi_runtime_free;
|
||||
|
||||
memset(&snd_card_asihpi_playback, 0, sizeof(snd_card_asihpi_playback));
|
||||
snd_card_asihpi_playback.buffer_bytes_max = BUFFER_BYTES_MAX;
|
||||
snd_card_asihpi_playback.period_bytes_min = PERIOD_BYTES_MIN;
|
||||
/*?snd_card_asihpi_playback.period_bytes_min =
|
||||
card->out_max_chans * 4096; */
|
||||
snd_card_asihpi_playback.period_bytes_max = BUFFER_BYTES_MAX / PERIODS_MIN;
|
||||
snd_card_asihpi_playback.periods_min = PERIODS_MIN;
|
||||
snd_card_asihpi_playback.periods_max = BUFFER_BYTES_MAX / PERIOD_BYTES_MIN;
|
||||
if (!card->hpi->interrupt_mode) {
|
||||
snd_card_asihpi_playback.buffer_bytes_max = BUFFER_BYTES_MAX;
|
||||
snd_card_asihpi_playback.period_bytes_min = PERIOD_BYTES_MIN;
|
||||
snd_card_asihpi_playback.period_bytes_max = BUFFER_BYTES_MAX / PERIODS_MIN;
|
||||
snd_card_asihpi_playback.periods_min = PERIODS_MIN;
|
||||
snd_card_asihpi_playback.periods_max = BUFFER_BYTES_MAX / PERIOD_BYTES_MIN;
|
||||
} else {
|
||||
size_t pbmin = card->update_interval_frames *
|
||||
card->out_max_chans;
|
||||
snd_card_asihpi_playback.buffer_bytes_max = BUFFER_BYTES_MAX;
|
||||
snd_card_asihpi_playback.period_bytes_min = pbmin;
|
||||
snd_card_asihpi_playback.period_bytes_max = BUFFER_BYTES_MAX / PERIODS_MIN;
|
||||
snd_card_asihpi_playback.periods_min = PERIODS_MIN;
|
||||
snd_card_asihpi_playback.periods_max = BUFFER_BYTES_MAX / pbmin;
|
||||
}
|
||||
|
||||
/* snd_card_asihpi_playback.fifo_size = 0; */
|
||||
snd_card_asihpi_playback.channels_max = card->out_max_chans;
|
||||
snd_card_asihpi_playback.channels_min = card->out_min_chans;
|
||||
|
@ -1036,7 +1122,7 @@ static int snd_card_asihpi_playback_open(struct snd_pcm_substream *substream)
|
|||
card->update_interval_frames);
|
||||
|
||||
snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_PERIOD_SIZE,
|
||||
card->update_interval_frames * 2, UINT_MAX);
|
||||
card->update_interval_frames, UINT_MAX);
|
||||
|
||||
snd_printdd("playback open\n");
|
||||
|
||||
|
@ -1102,8 +1188,6 @@ static int snd_card_asihpi_capture_prepare(struct snd_pcm_substream *substream)
|
|||
return 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
static u64 snd_card_asihpi_capture_formats(struct snd_card_asihpi *asihpi,
|
||||
u32 h_stream)
|
||||
{
|
||||
|
@ -1170,11 +1254,21 @@ static int snd_card_asihpi_capture_open(struct snd_pcm_substream *substream)
|
|||
runtime->private_free = snd_card_asihpi_runtime_free;
|
||||
|
||||
memset(&snd_card_asihpi_capture, 0, sizeof(snd_card_asihpi_capture));
|
||||
snd_card_asihpi_capture.buffer_bytes_max = BUFFER_BYTES_MAX;
|
||||
snd_card_asihpi_capture.period_bytes_min = PERIOD_BYTES_MIN;
|
||||
snd_card_asihpi_capture.period_bytes_max = BUFFER_BYTES_MAX / PERIODS_MIN;
|
||||
snd_card_asihpi_capture.periods_min = PERIODS_MIN;
|
||||
snd_card_asihpi_capture.periods_max = BUFFER_BYTES_MAX / PERIOD_BYTES_MIN;
|
||||
if (!card->hpi->interrupt_mode) {
|
||||
snd_card_asihpi_capture.buffer_bytes_max = BUFFER_BYTES_MAX;
|
||||
snd_card_asihpi_capture.period_bytes_min = PERIOD_BYTES_MIN;
|
||||
snd_card_asihpi_capture.period_bytes_max = BUFFER_BYTES_MAX / PERIODS_MIN;
|
||||
snd_card_asihpi_capture.periods_min = PERIODS_MIN;
|
||||
snd_card_asihpi_capture.periods_max = BUFFER_BYTES_MAX / PERIOD_BYTES_MIN;
|
||||
} else {
|
||||
size_t pbmin = card->update_interval_frames *
|
||||
card->out_max_chans;
|
||||
snd_card_asihpi_capture.buffer_bytes_max = BUFFER_BYTES_MAX;
|
||||
snd_card_asihpi_capture.period_bytes_min = pbmin;
|
||||
snd_card_asihpi_capture.period_bytes_max = BUFFER_BYTES_MAX / PERIODS_MIN;
|
||||
snd_card_asihpi_capture.periods_min = PERIODS_MIN;
|
||||
snd_card_asihpi_capture.periods_max = BUFFER_BYTES_MAX / pbmin;
|
||||
}
|
||||
/* snd_card_asihpi_capture.fifo_size = 0; */
|
||||
snd_card_asihpi_capture.channels_max = card->in_max_chans;
|
||||
snd_card_asihpi_capture.channels_min = card->in_min_chans;
|
||||
|
@ -1199,7 +1293,7 @@ static int snd_card_asihpi_capture_open(struct snd_pcm_substream *substream)
|
|||
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);
|
||||
card->update_interval_frames, UINT_MAX);
|
||||
|
||||
snd_pcm_set_sync(substream);
|
||||
|
||||
|
@ -2444,15 +2538,19 @@ static int snd_asihpi_clkrate_get(struct snd_kcontrol *kcontrol,
|
|||
static int snd_asihpi_sampleclock_add(struct snd_card_asihpi *asihpi,
|
||||
struct hpi_control *hpi_ctl)
|
||||
{
|
||||
struct snd_card *card = asihpi->card;
|
||||
struct snd_card *card;
|
||||
struct snd_kcontrol_new snd_control;
|
||||
|
||||
struct clk_cache *clkcache = &asihpi->cc;
|
||||
struct clk_cache *clkcache;
|
||||
u32 hSC = hpi_ctl->h_control;
|
||||
int has_aes_in = 0;
|
||||
int i, j;
|
||||
u16 source;
|
||||
|
||||
if (snd_BUG_ON(!asihpi))
|
||||
return -EINVAL;
|
||||
card = asihpi->card;
|
||||
clkcache = &asihpi->cc;
|
||||
snd_control.private_value = hpi_ctl->h_control;
|
||||
|
||||
clkcache->has_local = 0;
|
||||
|
@ -2808,6 +2906,7 @@ static int snd_asihpi_probe(struct pci_dev *pci_dev,
|
|||
asihpi->card = card;
|
||||
asihpi->pci = pci_dev;
|
||||
asihpi->hpi = hpi;
|
||||
hpi->snd_card = card;
|
||||
|
||||
snd_printk(KERN_INFO "adapter ID=%4X index=%d\n",
|
||||
asihpi->hpi->adapter->type, adapter_index);
|
||||
|
@ -2830,8 +2929,16 @@ static int snd_asihpi_probe(struct pci_dev *pci_dev,
|
|||
if (err)
|
||||
asihpi->update_interval_frames = 512;
|
||||
|
||||
if (!asihpi->can_dma)
|
||||
asihpi->update_interval_frames *= 2;
|
||||
if (hpi->interrupt_mode) {
|
||||
asihpi->pcm_start = snd_card_asihpi_pcm_int_start;
|
||||
asihpi->pcm_stop = snd_card_asihpi_pcm_int_stop;
|
||||
tasklet_init(&asihpi->t, snd_card_asihpi_int_task,
|
||||
(unsigned long)hpi);
|
||||
hpi->interrupt_callback = snd_card_asihpi_isr;
|
||||
} else {
|
||||
asihpi->pcm_start = snd_card_asihpi_pcm_timer_start;
|
||||
asihpi->pcm_stop = snd_card_asihpi_pcm_timer_stop;
|
||||
}
|
||||
|
||||
hpi_handle_error(hpi_instream_open(adapter_index,
|
||||
0, &h_stream));
|
||||
|
@ -2841,6 +2948,9 @@ static int snd_asihpi_probe(struct pci_dev *pci_dev,
|
|||
|
||||
hpi_handle_error(hpi_instream_close(h_stream));
|
||||
|
||||
if (!asihpi->can_dma)
|
||||
asihpi->update_interval_frames *= 2;
|
||||
|
||||
err = hpi_adapter_get_property(adapter_index,
|
||||
HPI_ADAPTER_PROPERTY_CURCHANNELS,
|
||||
&asihpi->in_max_chans, &asihpi->out_max_chans);
|
||||
|
@ -2900,7 +3010,6 @@ static int snd_asihpi_probe(struct pci_dev *pci_dev,
|
|||
err = snd_card_register(card);
|
||||
|
||||
if (!err) {
|
||||
hpi->snd_card = card;
|
||||
dev++;
|
||||
return 0;
|
||||
}
|
||||
|
@ -2914,6 +3023,16 @@ __nodev:
|
|||
static void snd_asihpi_remove(struct pci_dev *pci_dev)
|
||||
{
|
||||
struct hpi_adapter *hpi = pci_get_drvdata(pci_dev);
|
||||
struct snd_card_asihpi *asihpi = hpi->snd_card->private_data;
|
||||
|
||||
/* Stop interrupts */
|
||||
if (hpi->interrupt_mode) {
|
||||
hpi->interrupt_callback = NULL;
|
||||
hpi_handle_error(hpi_adapter_set_property(hpi->adapter->index,
|
||||
HPI_ADAPTER_PROPERTY_IRQ_RATE, 0, 0));
|
||||
tasklet_kill(&asihpi->t);
|
||||
}
|
||||
|
||||
snd_card_free(hpi->snd_card);
|
||||
hpi->snd_card = NULL;
|
||||
asihpi_adapter_remove(pci_dev);
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
/******************************************************************************
|
||||
|
||||
AudioScience HPI driver
|
||||
Copyright (C) 1997-2011 AudioScience Inc. <support@audioscience.com>
|
||||
Copyright (C) 1997-2014 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
|
||||
|
@ -163,6 +163,9 @@ static u16 create_adapter_obj(struct hpi_adapter_obj *pao,
|
|||
|
||||
static void delete_adapter_obj(struct hpi_adapter_obj *pao);
|
||||
|
||||
static int adapter_irq_query_and_clear(struct hpi_adapter_obj *pao,
|
||||
u32 message);
|
||||
|
||||
static void outstream_host_buffer_allocate(struct hpi_adapter_obj *pao,
|
||||
struct hpi_message *phm, struct hpi_response *phr);
|
||||
|
||||
|
@ -283,7 +286,6 @@ static void adapter_message(struct hpi_adapter_obj *pao,
|
|||
case HPI_ADAPTER_DELETE:
|
||||
adapter_delete(pao, phm, phr);
|
||||
break;
|
||||
|
||||
default:
|
||||
hw_message(pao, phm, phr);
|
||||
break;
|
||||
|
@ -673,6 +675,12 @@ static u16 create_adapter_obj(struct hpi_adapter_obj *pao,
|
|||
|
||||
HPI_DEBUG_LOG(INFO, "bootload DSP OK\n");
|
||||
|
||||
pao->irq_query_and_clear = adapter_irq_query_and_clear;
|
||||
pao->instream_host_buffer_status =
|
||||
phw->p_interface_buffer->instream_host_buffer_status;
|
||||
pao->outstream_host_buffer_status =
|
||||
phw->p_interface_buffer->outstream_host_buffer_status;
|
||||
|
||||
return hpi_add_adapter(pao);
|
||||
}
|
||||
|
||||
|
@ -713,6 +721,21 @@ static void delete_adapter_obj(struct hpi_adapter_obj *pao)
|
|||
|
||||
/*****************************************************************************/
|
||||
/* Adapter functions */
|
||||
static int adapter_irq_query_and_clear(struct hpi_adapter_obj *pao,
|
||||
u32 message)
|
||||
{
|
||||
struct hpi_hw_obj *phw = pao->priv;
|
||||
u32 hsr = 0;
|
||||
|
||||
hsr = ioread32(phw->prHSR);
|
||||
if (hsr & C6205_HSR_INTSRC) {
|
||||
/* reset the interrupt from the DSP */
|
||||
iowrite32(C6205_HSR_INTSRC, phw->prHSR);
|
||||
return HPI_IRQ_MIXER;
|
||||
}
|
||||
|
||||
return HPI_IRQ_NONE;
|
||||
}
|
||||
|
||||
/*****************************************************************************/
|
||||
/* OutStream Host buffer functions */
|
||||
|
@ -1331,17 +1354,21 @@ static u16 adapter_boot_load_dsp(struct hpi_adapter_obj *pao,
|
|||
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);
|
||||
boot_loader_write_mem32(pao, 0, 0x018C0024, 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);
|
||||
|
||||
/* value of bit 3 is unknown after DSP reset, other bits shoudl be 0 */
|
||||
if (0 != (boot_loader_read_mem32(pao, 0,
|
||||
(C6205_BAR0_TIMER1_CTL)) & ~8))
|
||||
return HPI6205_ERROR_6205_REG;
|
||||
hpios_delay_micro_seconds(100);
|
||||
|
||||
/* Release C6713 from reset - revB */
|
||||
boot_loader_write_mem32(pao, 0, C6205_BAR0_TIMER1_CTL, 4);
|
||||
if (4 != (boot_loader_read_mem32(pao, 0,
|
||||
(C6205_BAR0_TIMER1_CTL)) & ~8))
|
||||
return HPI6205_ERROR_6205_REG;
|
||||
hpios_delay_micro_seconds(100);
|
||||
}
|
||||
|
||||
|
@ -2089,7 +2116,7 @@ static u16 message_response_sequence(struct hpi_adapter_obj *pao,
|
|||
return 0;
|
||||
}
|
||||
|
||||
/* Assume buffer of type struct bus_master_interface
|
||||
/* Assume buffer of type struct bus_master_interface_62
|
||||
is allocated "noncacheable" */
|
||||
|
||||
if (!wait_dsp_ack(phw, H620_HIF_IDLE, HPI6205_TIMEOUT)) {
|
||||
|
|
|
@ -686,8 +686,8 @@ union hpi_adapterx_msg {
|
|||
u16 value;
|
||||
} test_assert;
|
||||
struct {
|
||||
u32 yes;
|
||||
} irq_query;
|
||||
u32 message;
|
||||
} irq;
|
||||
u32 pad[3];
|
||||
};
|
||||
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
/**
|
||||
|
||||
AudioScience HPI driver
|
||||
Copyright (C) 1997-2011 AudioScience Inc. <support@audioscience.com>
|
||||
Copyright (C) 1997-2014 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
|
||||
|
@ -21,7 +21,11 @@
|
|||
struct hpi_adapter_obj;
|
||||
|
||||
/* a function that takes an adapter obj and returns an int */
|
||||
typedef int adapter_int_func(struct hpi_adapter_obj *pao);
|
||||
typedef int adapter_int_func(struct hpi_adapter_obj *pao, u32 message);
|
||||
|
||||
#define HPI_IRQ_NONE (0)
|
||||
#define HPI_IRQ_MESSAGE (1)
|
||||
#define HPI_IRQ_MIXER (2)
|
||||
|
||||
struct hpi_adapter_obj {
|
||||
struct hpi_pci pci; /* PCI info - bus#,dev#,address etc */
|
||||
|
@ -33,6 +37,9 @@ struct hpi_adapter_obj {
|
|||
u16 dsp_crashed;
|
||||
u16 has_control_cache;
|
||||
void *priv;
|
||||
adapter_int_func *irq_query_and_clear;
|
||||
struct hpi_hostbuffer_status *instream_host_buffer_status;
|
||||
struct hpi_hostbuffer_status *outstream_host_buffer_status;
|
||||
};
|
||||
|
||||
struct hpi_control_cache {
|
||||
|
@ -55,13 +62,21 @@ 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);
|
||||
|
||||
short hpi_check_control_cache_single(struct hpi_control_cache_single *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, u8 *pDSP_control_buffer);
|
||||
|
||||
void hpi_free_control_cache(struct hpi_control_cache *p_cache);
|
||||
|
||||
void hpi_cmn_control_cache_sync_to_msg(struct hpi_control_cache *pC,
|
||||
struct hpi_message *phm, struct hpi_response *phr);
|
||||
|
||||
void hpi_cmn_control_cache_sync_to_msg_single(struct hpi_control_cache_single
|
||||
*pC, struct hpi_message *phm, struct hpi_response *phr);
|
||||
|
||||
u16 hpi_validate_response(struct hpi_message *phm, struct hpi_response *phr);
|
||||
|
||||
hpi_handler_func HPI_COMMON;
|
||||
|
|
|
@ -1,7 +1,8 @@
|
|||
/*******************************************************************************
|
||||
|
||||
AudioScience HPI driver
|
||||
Copyright (C) 1997-2011 AudioScience Inc. <support@audioscience.com>
|
||||
Common Linux HPI ioctl and module probe/remove functions
|
||||
|
||||
Copyright (C) 1997-2014 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
|
||||
|
@ -12,11 +13,6 @@
|
|||
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"
|
||||
|
||||
|
@ -29,6 +25,7 @@ Common Linux HPI ioctl and module probe/remove functions
|
|||
#include "hpicmn.h"
|
||||
|
||||
#include <linux/fs.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/moduleparam.h>
|
||||
#include <asm/uaccess.h>
|
||||
|
@ -307,10 +304,38 @@ out:
|
|||
return err;
|
||||
}
|
||||
|
||||
static int asihpi_irq_count;
|
||||
|
||||
static irqreturn_t asihpi_isr(int irq, void *dev_id)
|
||||
{
|
||||
struct hpi_adapter *a = dev_id;
|
||||
int handled;
|
||||
|
||||
if (!a->adapter->irq_query_and_clear) {
|
||||
pr_err("asihpi_isr ASI%04X:%d no handler\n", a->adapter->type,
|
||||
a->adapter->index);
|
||||
return IRQ_NONE;
|
||||
}
|
||||
|
||||
handled = a->adapter->irq_query_and_clear(a->adapter, 0);
|
||||
|
||||
if (!handled)
|
||||
return IRQ_NONE;
|
||||
|
||||
asihpi_irq_count++;
|
||||
/* printk(KERN_INFO "asihpi_isr %d ASI%04X:%d irq handled\n",
|
||||
asihpi_irq_count, a->adapter->type, a->adapter->index); */
|
||||
|
||||
if (a->interrupt_callback)
|
||||
a->interrupt_callback(a);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
int asihpi_adapter_probe(struct pci_dev *pci_dev,
|
||||
const struct pci_device_id *pci_id)
|
||||
{
|
||||
int idx, nm;
|
||||
int idx, nm, low_latency_mode = 0, irq_supported = 0;
|
||||
int adapter_index;
|
||||
unsigned int memlen;
|
||||
struct hpi_message hm;
|
||||
|
@ -388,8 +413,39 @@ int asihpi_adapter_probe(struct pci_dev *pci_dev,
|
|||
hm.adapter_index = adapter.adapter->index;
|
||||
hpi_send_recv_ex(&hm, &hr, HOWNER_KERNEL);
|
||||
|
||||
if (hr.error)
|
||||
if (hr.error) {
|
||||
HPI_DEBUG_LOG(ERROR, "HPI_ADAPTER_OPEN failed, aborting\n");
|
||||
goto err;
|
||||
}
|
||||
|
||||
/* Check if current mode == Low Latency mode */
|
||||
hpi_init_message_response(&hm, &hr, HPI_OBJ_ADAPTER,
|
||||
HPI_ADAPTER_GET_MODE);
|
||||
hm.adapter_index = adapter.adapter->index;
|
||||
hpi_send_recv_ex(&hm, &hr, HOWNER_KERNEL);
|
||||
|
||||
if (hr.error) {
|
||||
HPI_DEBUG_LOG(ERROR,
|
||||
"HPI_ADAPTER_GET_MODE failed, aborting\n");
|
||||
goto err;
|
||||
}
|
||||
|
||||
if (hr.u.ax.mode.adapter_mode == HPI_ADAPTER_MODE_LOW_LATENCY)
|
||||
low_latency_mode = 1;
|
||||
|
||||
/* Check if IRQs are supported */
|
||||
hpi_init_message_response(&hm, &hr, HPI_OBJ_ADAPTER,
|
||||
HPI_ADAPTER_GET_PROPERTY);
|
||||
hm.adapter_index = adapter.adapter->index;
|
||||
hm.u.ax.property_set.property = HPI_ADAPTER_PROPERTY_SUPPORTS_IRQ;
|
||||
hpi_send_recv_ex(&hm, &hr, HOWNER_KERNEL);
|
||||
if (hr.error || !hr.u.ax.property_get.parameter1) {
|
||||
dev_info(&pci_dev->dev,
|
||||
"IRQs not supported by adapter at index %d\n",
|
||||
adapter.adapter->index);
|
||||
} else {
|
||||
irq_supported = 1;
|
||||
}
|
||||
|
||||
/* WARNING can't init mutex in 'adapter'
|
||||
* and then copy it to adapters[] ?!?!
|
||||
|
@ -398,6 +454,44 @@ int asihpi_adapter_probe(struct pci_dev *pci_dev,
|
|||
mutex_init(&adapters[adapter_index].mutex);
|
||||
pci_set_drvdata(pci_dev, &adapters[adapter_index]);
|
||||
|
||||
if (low_latency_mode && irq_supported) {
|
||||
if (!adapter.adapter->irq_query_and_clear) {
|
||||
dev_err(&pci_dev->dev,
|
||||
"no IRQ handler for adapter %d, aborting\n",
|
||||
adapter.adapter->index);
|
||||
goto err;
|
||||
}
|
||||
|
||||
/* Disable IRQ generation on DSP side by setting the rate to 0 */
|
||||
hpi_init_message_response(&hm, &hr, HPI_OBJ_ADAPTER,
|
||||
HPI_ADAPTER_SET_PROPERTY);
|
||||
hm.adapter_index = adapter.adapter->index;
|
||||
hm.u.ax.property_set.property = HPI_ADAPTER_PROPERTY_IRQ_RATE;
|
||||
hm.u.ax.property_set.parameter1 = 0;
|
||||
hm.u.ax.property_set.parameter2 = 0;
|
||||
hpi_send_recv_ex(&hm, &hr, HOWNER_KERNEL);
|
||||
if (hr.error) {
|
||||
HPI_DEBUG_LOG(ERROR,
|
||||
"HPI_ADAPTER_GET_MODE failed, aborting\n");
|
||||
goto err;
|
||||
}
|
||||
|
||||
/* Note: request_irq calls asihpi_isr here */
|
||||
if (request_irq(pci_dev->irq, asihpi_isr, IRQF_SHARED,
|
||||
"asihpi", &adapters[adapter_index])) {
|
||||
dev_err(&pci_dev->dev, "request_irq(%d) failed\n",
|
||||
pci_dev->irq);
|
||||
goto err;
|
||||
}
|
||||
|
||||
adapters[adapter_index].interrupt_mode = 1;
|
||||
|
||||
dev_info(&pci_dev->dev, "using irq %d\n", pci_dev->irq);
|
||||
adapters[adapter_index].irq = pci_dev->irq;
|
||||
} else {
|
||||
dev_info(&pci_dev->dev, "using polled mode\n");
|
||||
}
|
||||
|
||||
dev_info(&pci_dev->dev, "probe succeeded for ASI%04X HPI index %d\n",
|
||||
adapter.adapter->type, adapter_index);
|
||||
|
||||
|
@ -431,6 +525,15 @@ void asihpi_adapter_remove(struct pci_dev *pci_dev)
|
|||
pa = pci_get_drvdata(pci_dev);
|
||||
pci = pa->adapter->pci;
|
||||
|
||||
/* Disable IRQ generation on DSP side */
|
||||
hpi_init_message_response(&hm, &hr, HPI_OBJ_ADAPTER,
|
||||
HPI_ADAPTER_SET_PROPERTY);
|
||||
hm.adapter_index = pa->adapter->index;
|
||||
hm.u.ax.property_set.property = HPI_ADAPTER_PROPERTY_IRQ_RATE;
|
||||
hm.u.ax.property_set.parameter1 = 0;
|
||||
hm.u.ax.property_set.parameter2 = 0;
|
||||
hpi_send_recv_ex(&hm, &hr, HOWNER_KERNEL);
|
||||
|
||||
hpi_init_message_response(&hm, &hr, HPI_OBJ_ADAPTER,
|
||||
HPI_ADAPTER_DELETE);
|
||||
hm.adapter_index = pa->adapter->index;
|
||||
|
@ -442,6 +545,9 @@ void asihpi_adapter_remove(struct pci_dev *pci_dev)
|
|||
iounmap(pci.ap_mem_base[idx]);
|
||||
}
|
||||
|
||||
if (pa->irq)
|
||||
free_irq(pa->irq, pa);
|
||||
|
||||
if (pa->p_buffer)
|
||||
vfree(pa->p_buffer);
|
||||
|
||||
|
|
|
@ -151,6 +151,10 @@ struct hpi_adapter {
|
|||
struct hpi_adapter_obj *adapter;
|
||||
struct snd_card *snd_card;
|
||||
|
||||
int irq;
|
||||
int interrupt_mode;
|
||||
void (*interrupt_callback) (struct hpi_adapter *);
|
||||
|
||||
/* mutex prevents contention for one card
|
||||
between multiple user programs (via ioctl) */
|
||||
struct mutex mutex;
|
||||
|
|
Loading…
Reference in a new issue