Commit f9a376c3 authored by Eliot Blennerhassett's avatar Eliot Blennerhassett Committed by Takashi Iwai

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: default avatarEliot Blennerhassett <eliot@blennerhassett.gen.nz>
Signed-off-by: default avatarTakashi Iwai <tiwai@suse.de>
parent c1464a88
This diff is collapsed.
/****************************************************************************** /******************************************************************************
AudioScience HPI driver 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 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 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, ...@@ -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 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, static void outstream_host_buffer_allocate(struct hpi_adapter_obj *pao,
struct hpi_message *phm, struct hpi_response *phr); struct hpi_message *phm, struct hpi_response *phr);
...@@ -283,7 +286,6 @@ static void adapter_message(struct hpi_adapter_obj *pao, ...@@ -283,7 +286,6 @@ static void adapter_message(struct hpi_adapter_obj *pao,
case HPI_ADAPTER_DELETE: case HPI_ADAPTER_DELETE:
adapter_delete(pao, phm, phr); adapter_delete(pao, phm, phr);
break; break;
default: default:
hw_message(pao, phm, phr); hw_message(pao, phm, phr);
break; break;
...@@ -673,6 +675,12 @@ static u16 create_adapter_obj(struct hpi_adapter_obj *pao, ...@@ -673,6 +675,12 @@ static u16 create_adapter_obj(struct hpi_adapter_obj *pao,
HPI_DEBUG_LOG(INFO, "bootload DSP OK\n"); 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); return hpi_add_adapter(pao);
} }
...@@ -713,6 +721,21 @@ static void delete_adapter_obj(struct hpi_adapter_obj *pao) ...@@ -713,6 +721,21 @@ static void delete_adapter_obj(struct hpi_adapter_obj *pao)
/*****************************************************************************/ /*****************************************************************************/
/* Adapter functions */ /* 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 */ /* OutStream Host buffer functions */
...@@ -1331,17 +1354,21 @@ static u16 adapter_boot_load_dsp(struct hpi_adapter_obj *pao, ...@@ -1331,17 +1354,21 @@ static u16 adapter_boot_load_dsp(struct hpi_adapter_obj *pao,
if (boot_code_id[1] != 0) { if (boot_code_id[1] != 0) {
/* DSP 1 is a C6713 */ /* DSP 1 is a C6713 */
/* CLKX0 <- '1' release the C6205 bootmode pulldowns */ /* 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); hpios_delay_micro_seconds(100);
/* Reset the 6713 #1 - revB */ /* Reset the 6713 #1 - revB */
boot_loader_write_mem32(pao, 0, C6205_BAR0_TIMER1_CTL, 0); boot_loader_write_mem32(pao, 0, C6205_BAR0_TIMER1_CTL, 0);
/* value of bit 3 is unknown after DSP reset, other bits shoudl be 0 */
/* dummy read every 4 words for 6205 advisory 1.4.4 */ if (0 != (boot_loader_read_mem32(pao, 0,
boot_loader_read_mem32(pao, 0, 0); (C6205_BAR0_TIMER1_CTL)) & ~8))
return HPI6205_ERROR_6205_REG;
hpios_delay_micro_seconds(100); hpios_delay_micro_seconds(100);
/* Release C6713 from reset - revB */ /* Release C6713 from reset - revB */
boot_loader_write_mem32(pao, 0, C6205_BAR0_TIMER1_CTL, 4); 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); hpios_delay_micro_seconds(100);
} }
...@@ -2089,7 +2116,7 @@ static u16 message_response_sequence(struct hpi_adapter_obj *pao, ...@@ -2089,7 +2116,7 @@ static u16 message_response_sequence(struct hpi_adapter_obj *pao,
return 0; return 0;
} }
/* Assume buffer of type struct bus_master_interface /* Assume buffer of type struct bus_master_interface_62
is allocated "noncacheable" */ is allocated "noncacheable" */
if (!wait_dsp_ack(phw, H620_HIF_IDLE, HPI6205_TIMEOUT)) { if (!wait_dsp_ack(phw, H620_HIF_IDLE, HPI6205_TIMEOUT)) {
......
...@@ -686,8 +686,8 @@ union hpi_adapterx_msg { ...@@ -686,8 +686,8 @@ union hpi_adapterx_msg {
u16 value; u16 value;
} test_assert; } test_assert;
struct { struct {
u32 yes; u32 message;
} irq_query; } irq;
u32 pad[3]; u32 pad[3];
}; };
......
/** /**
AudioScience HPI driver 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 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 it under the terms of version 2 of the GNU General Public License as
...@@ -21,7 +21,11 @@ ...@@ -21,7 +21,11 @@
struct hpi_adapter_obj; struct hpi_adapter_obj;
/* a function that takes an adapter obj and returns an int */ /* 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_adapter_obj {
struct hpi_pci pci; /* PCI info - bus#,dev#,address etc */ struct hpi_pci pci; /* PCI info - bus#,dev#,address etc */
...@@ -33,6 +37,9 @@ struct hpi_adapter_obj { ...@@ -33,6 +37,9 @@ struct hpi_adapter_obj {
u16 dsp_crashed; u16 dsp_crashed;
u16 has_control_cache; u16 has_control_cache;
void *priv; 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 { struct hpi_control_cache {
...@@ -55,13 +62,21 @@ void hpi_delete_adapter(struct hpi_adapter_obj *pao); ...@@ -55,13 +62,21 @@ void hpi_delete_adapter(struct hpi_adapter_obj *pao);
short hpi_check_control_cache(struct hpi_control_cache *pC, short hpi_check_control_cache(struct hpi_control_cache *pC,
struct hpi_message *phm, struct hpi_response *phr); 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 struct hpi_control_cache *hpi_alloc_control_cache(const u32
number_of_controls, const u32 size_in_bytes, u8 *pDSP_control_buffer); 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_free_control_cache(struct hpi_control_cache *p_cache);
void hpi_cmn_control_cache_sync_to_msg(struct hpi_control_cache *pC, void hpi_cmn_control_cache_sync_to_msg(struct hpi_control_cache *pC,
struct hpi_message *phm, struct hpi_response *phr); 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); u16 hpi_validate_response(struct hpi_message *phm, struct hpi_response *phr);
hpi_handler_func HPI_COMMON; hpi_handler_func HPI_COMMON;
/******************************************************************************* /*******************************************************************************
AudioScience HPI driver 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 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 it under the terms of version 2 of the GNU General Public License as
...@@ -12,11 +13,6 @@ ...@@ -12,11 +13,6 @@
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details. 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" #define SOURCEFILE_NAME "hpioctl.c"
...@@ -29,6 +25,7 @@ Common Linux HPI ioctl and module probe/remove functions ...@@ -29,6 +25,7 @@ Common Linux HPI ioctl and module probe/remove functions
#include "hpicmn.h" #include "hpicmn.h"
#include <linux/fs.h> #include <linux/fs.h>
#include <linux/interrupt.h>
#include <linux/slab.h> #include <linux/slab.h>
#include <linux/moduleparam.h> #include <linux/moduleparam.h>
#include <asm/uaccess.h> #include <asm/uaccess.h>
...@@ -307,10 +304,38 @@ long asihpi_hpi_ioctl(struct file *file, unsigned int cmd, unsigned long arg) ...@@ -307,10 +304,38 @@ long asihpi_hpi_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
return err; 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, int asihpi_adapter_probe(struct pci_dev *pci_dev,
const struct pci_device_id *pci_id) const struct pci_device_id *pci_id)
{ {
int idx, nm; int idx, nm, low_latency_mode = 0, irq_supported = 0;
int adapter_index; int adapter_index;
unsigned int memlen; unsigned int memlen;
struct hpi_message hm; struct hpi_message hm;
...@@ -388,8 +413,39 @@ int asihpi_adapter_probe(struct pci_dev *pci_dev, ...@@ -388,8 +413,39 @@ int asihpi_adapter_probe(struct pci_dev *pci_dev,
hm.adapter_index = adapter.adapter->index; hm.adapter_index = adapter.adapter->index;
hpi_send_recv_ex(&hm, &hr, HOWNER_KERNEL); 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; 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' /* WARNING can't init mutex in 'adapter'
* and then copy it to adapters[] ?!?! * and then copy it to adapters[] ?!?!
...@@ -398,6 +454,44 @@ int asihpi_adapter_probe(struct pci_dev *pci_dev, ...@@ -398,6 +454,44 @@ int asihpi_adapter_probe(struct pci_dev *pci_dev,
mutex_init(&adapters[adapter_index].mutex); mutex_init(&adapters[adapter_index].mutex);
pci_set_drvdata(pci_dev, &adapters[adapter_index]); 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", dev_info(&pci_dev->dev, "probe succeeded for ASI%04X HPI index %d\n",
adapter.adapter->type, adapter_index); adapter.adapter->type, adapter_index);
...@@ -431,6 +525,15 @@ void asihpi_adapter_remove(struct pci_dev *pci_dev) ...@@ -431,6 +525,15 @@ void asihpi_adapter_remove(struct pci_dev *pci_dev)
pa = pci_get_drvdata(pci_dev); pa = pci_get_drvdata(pci_dev);
pci = pa->adapter->pci; 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_init_message_response(&hm, &hr, HPI_OBJ_ADAPTER,
HPI_ADAPTER_DELETE); HPI_ADAPTER_DELETE);
hm.adapter_index = pa->adapter->index; hm.adapter_index = pa->adapter->index;
...@@ -442,6 +545,9 @@ void asihpi_adapter_remove(struct pci_dev *pci_dev) ...@@ -442,6 +545,9 @@ void asihpi_adapter_remove(struct pci_dev *pci_dev)
iounmap(pci.ap_mem_base[idx]); iounmap(pci.ap_mem_base[idx]);
} }
if (pa->irq)
free_irq(pa->irq, pa);
if (pa->p_buffer) if (pa->p_buffer)
vfree(pa->p_buffer); vfree(pa->p_buffer);
......
...@@ -151,6 +151,10 @@ struct hpi_adapter { ...@@ -151,6 +151,10 @@ struct hpi_adapter {
struct hpi_adapter_obj *adapter; struct hpi_adapter_obj *adapter;
struct snd_card *snd_card; struct snd_card *snd_card;
int irq;
int interrupt_mode;
void (*interrupt_callback) (struct hpi_adapter *);
/* mutex prevents contention for one card /* mutex prevents contention for one card
between multiple user programs (via ioctl) */ between multiple user programs (via ioctl) */
struct mutex mutex; struct mutex mutex;
......
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