ALSA: asihpi: Add support for stream interrupt.
[deliverable/linux.git] / sound / pci / asihpi / hpioctl.c
index 7f0272032fbb2b25e8c8104523b41a7a5dc82f04..9454932fc9c0a45d2bf0bc875f7244e57fb70a52 100644 (file)
@@ -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
     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);
 
This page took 0.026159 seconds and 5 git commands to generate.