From fdccbdfdafad6de80ae651758a3d01e8433ee5d8 Mon Sep 17 00:00:00 2001
From: Pierre-Louis Bossart <pierre-louis.bossart@intel.com>
Date: Fri, 14 May 2010 15:38:18 -0500
Subject: [PATCH] alsa: support for disabling of period interrupts


Signed-off-by: Pierre-Louis Bossart <pierre-louis.bossart@intel.com>
---
 include/sound/asound.h    |    1 +
 include/sound/pcm.h       |    2 ++
 sound/core/pcm_lib.c      |   22 ++++++++++++----------
 sound/core/pcm_native.c   |    7 ++++++-
 sound/pci/hda/hda_intel.c |   28 +++++++++++++++++++++++++---
 5 files changed, 46 insertions(+), 14 deletions(-)

diff --git a/include/sound/asound.h b/include/sound/asound.h
index 0985955..8d9e97a 100644
--- a/include/sound/asound.h
+++ b/include/sound/asound.h
@@ -330,6 +330,7 @@ typedef int snd_pcm_hw_param_t;
 #define	SNDRV_PCM_HW_PARAM_LAST_INTERVAL	SNDRV_PCM_HW_PARAM_TICK_TIME
 
 #define SNDRV_PCM_HW_PARAMS_NORESAMPLE	(1<<0)	/* avoid rate resampling */
+#define SNDRV_PCM_HW_PARAMS_DISABLE_PERIOD_INTERRUPTS	(1<<2)	/* disable period interrupts */
 
 struct snd_interval {
 	unsigned int min, max;
diff --git a/include/sound/pcm.h b/include/sound/pcm.h
index 8b611a5..a5ec8c1 100644
--- a/include/sound/pcm.h
+++ b/include/sound/pcm.h
@@ -55,6 +55,7 @@ struct snd_pcm_hardware {
 	unsigned int periods_min;	/* min # of periods */
 	unsigned int periods_max;	/* max # of periods */
 	size_t fifo_size;		/* fifo size in bytes */
+        unsigned int can_disable_period_interrupts;
 };
 
 struct snd_pcm_substream;
@@ -276,6 +277,7 @@ struct snd_pcm_runtime {
 	snd_pcm_sframes_t delay;	/* extra delay; typically FIFO size */
 
 	/* -- HW params -- */
+        unsigned int flags;
 	snd_pcm_access_t access;	/* access mode */
 	snd_pcm_format_t format;	/* SNDRV_PCM_FORMAT_* */
 	snd_pcm_subformat_t subformat;	/* subformat */
diff --git a/sound/core/pcm_lib.c b/sound/core/pcm_lib.c
index a2ff861..ea5194e 100644
--- a/sound/core/pcm_lib.c
+++ b/sound/core/pcm_lib.c
@@ -420,16 +420,18 @@ static int snd_pcm_update_hw_ptr0(struct snd_pcm_substream *substream,
 		hw_base = new_hw_ptr - (new_hw_ptr % runtime->buffer_size);
 	}
  no_jiffies_check:
-	if (delta > runtime->period_size + runtime->period_size / 2) {
-		hw_ptr_error(substream,
-			     "Lost interrupts? %s"
-			     "(stream=%i, delta=%ld, new_hw_ptr=%ld, "
-			     "old_hw_ptr=%ld)\n",
-			     in_interrupt ? "[Q] " : "",
-			     substream->stream, (long)delta,
-			     (long)new_hw_ptr,
-			     (long)old_hw_ptr);
-	}
+        if (!(runtime->flags & SNDRV_PCM_HW_PARAMS_DISABLE_PERIOD_INTERRUPTS)) {
+                if (delta > runtime->period_size + runtime->period_size / 2) {
+                        hw_ptr_error(substream,
+                                     "Lost interrupts? %s"
+                                     "(stream=%i, delta=%ld, new_hw_ptr=%ld, "
+                                     "old_hw_ptr=%ld)\n",
+                                     in_interrupt ? "[Q] " : "",
+                                     substream->stream, (long)delta,
+                                     (long)new_hw_ptr,
+                                     (long)old_hw_ptr);
+                }
+        }
 
 	if (runtime->status->hw_ptr == new_hw_ptr)
 		return 0;
diff --git a/sound/core/pcm_native.c b/sound/core/pcm_native.c
index c22ebb0..135131c 100644
--- a/sound/core/pcm_native.c
+++ b/sound/core/pcm_native.c
@@ -427,7 +427,7 @@ static int snd_pcm_hw_params(struct snd_pcm_substream *substream,
 		if (atomic_read(&substream->mmap_count))
 			return -EBADFD;
 
-	params->rmask = ~0U;
+        params->rmask = ~0U;
 	err = snd_pcm_hw_refine(substream, params);
 	if (err < 0)
 		goto _error;
@@ -442,6 +442,11 @@ static int snd_pcm_hw_params(struct snd_pcm_substream *substream,
 			goto _error;
 	}
 
+        if (substream->runtime->hw.can_disable_period_interrupts) {
+                runtime->flags  |= (params->flags&SNDRV_PCM_HW_PARAMS_DISABLE_PERIOD_INTERRUPTS);
+        } else {
+                runtime->flags &= ~SNDRV_PCM_HW_PARAMS_DISABLE_PERIOD_INTERRUPTS;
+        }
 	runtime->access = params_access(params);
 	runtime->format = params_format(params);
 	runtime->subformat = params_subformat(params);
diff --git a/sound/pci/hda/hda_intel.c b/sound/pci/hda/hda_intel.c
index 170610e..7360b37 100644
--- a/sound/pci/hda/hda_intel.c
+++ b/sound/pci/hda/hda_intel.c
@@ -116,6 +116,12 @@ module_param(power_save_controller, bool, 0644);
 MODULE_PARM_DESC(power_save_controller, "Reset controller in power save mode.");
 #endif
 
+static int disable_period_interrupts = 1;
+module_param(disable_period_interrupts, bool, 0644);
+MODULE_PARM_DESC(disable_period_interrupts, "Disable period interrupts"
+		 "(default true, set to false to force interrupts).");
+
+
 MODULE_LICENSE("GPL");
 MODULE_SUPPORTED_DEVICE("{{Intel, ICH6},"
 			 "{Intel, ICH6M},"
@@ -1157,7 +1163,12 @@ static int setup_bdle(struct snd_pcm_substream *substream,
 {
 	u32 *bdl = *bdlp;
 
-	while (size > 0) {
+        if (substream->runtime->flags & SNDRV_PCM_HW_PARAMS_DISABLE_PERIOD_INTERRUPTS) {
+                /* override ioc */
+                with_ioc = 0;
+        }
+
+        while (size > 0) {
 		dma_addr_t addr;
 		int chunk;
 
@@ -1519,6 +1530,7 @@ static struct snd_pcm_hardware azx_pcm_hw = {
 	.periods_min =		2,
 	.periods_max =		AZX_MAX_FRAG,
 	.fifo_size =		0,
+        .can_disable_period_interrupts =   1,
 };
 
 struct azx_pcm {
@@ -1554,7 +1566,13 @@ static int azx_pcm_open(struct snd_pcm_substream *substream)
 				   128);
 	snd_pcm_hw_constraint_step(runtime, 0, SNDRV_PCM_HW_PARAM_PERIOD_BYTES,
 				   128);
-	snd_hda_power_up(apcm->codec);
+
+        /* check if module parameter prevents interrupt disable */
+        if (disable_period_interrupts == 0) {
+                runtime->hw.can_disable_period_interrupts = 0;
+        }
+
+        snd_hda_power_up(apcm->codec);
 	err = hinfo->ops.open(hinfo, apcm->codec, substream);
 	if (err < 0) {
 		azx_release_device(azx_dev);
@@ -1947,7 +1965,11 @@ static void azx_irq_pending_work(struct work_struct *work)
 			if (ok > 0) {
 				azx_dev->irq_pending = 0;
 				spin_unlock(&chip->reg_lock);
-				snd_pcm_period_elapsed(azx_dev->substream);
+                                if( azx_dev->substream->runtime->flags & SNDRV_PCM_HW_PARAMS_DISABLE_PERIOD_INTERRUPTS)
+                                        printk(KERN_WARNING
+                                               "hda-intel: IRQ received but period interrupts were disabled. This should not happen \n");
+                                else
+                                        snd_pcm_period_elapsed(azx_dev->substream);
 				spin_lock(&chip->reg_lock);
 			} else if (ok < 0) {
 				pending = 0;	/* too early */
-- 
1.6.6.1

