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:

committed by
Takashi Iwai

parent
c1464a8854
commit
f9a376c3f6
@@ -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);
|
||||
|
||||
|
Reference in New Issue
Block a user