Hi Kristen,

Could you review and integrate the 2 patches which are part of fix for BOO
10899 (pls don't change the status of 10899, as it needs some more fixes
from other modules). 

0001: a back ported patch to disable period wakeup in ALSA kernel
0002: add disabling period wakeup in SST driver.

Thanks,
Feng
--------------------

>From e58d2883163e2b9840fd7ad59ad58684539da7ad Mon Sep 17 00:00:00 2001
From: Feng Tang <[email protected]>
Date: Fri, 29 Jul 2011 14:15:41 +0800
Subject: [PATCH 1/2] ALSA: pcm: support for period wakeup disabling

This patch allows to disable period interrupts which are
not needed when the application relies on a system timer
to wake-up and refill the ring buffer. The behavior of
the driver is left unchanged, and interrupts are only
disabled if the application requests this configuration.
The behavior in case of underruns is slightly different,
instead of being detected during the period interrupts the
underruns are detected when the application calls
snd_pcm_update_avail, which in turns forces a refresh of the
hw pointer and shows the buffer is empty.

More specifically this patch makes a lot of sense when
PulseAudio relies on timer-based scheduling to access audio
devices such as HDAudio or Intel SST. Disabling interrupts
removes two unwanted wake-ups due to period elapsed events
in low-power playback modes. It also simplifies PulseAudio
voice modules used for speech calls.

To quote Lennart "This patch looks very interesting and
desirable. This is something have long been waiting for."

Support for this in hardware drivers is optional.

[feng: also squash some new commits from upstream regarding
 disabling the period wakeup]

Signed-off-by: Pierre-Louis Bossart <[email protected]>
Signed-off-by: Clemens Ladisch <[email protected]>
Signed-off-by: Takashi Iwai <[email protected]>
---
 include/sound/asound.h  |    3 +++
 include/sound/pcm.h     |    1 +
 sound/core/pcm_lib.c    |   41 +++++++++++++++++++++++++++++++++++++----
 sound/core/pcm_native.c |   16 ++++++++++++++--
 4 files changed, 55 insertions(+), 6 deletions(-)

diff --git a/include/sound/asound.h b/include/sound/asound.h
index a1803ec..5d6074f 100644
--- a/include/sound/asound.h
+++ b/include/sound/asound.h
@@ -259,6 +259,7 @@ typedef int __bitwise snd_pcm_subformat_t;
 #define SNDRV_PCM_INFO_HALF_DUPLEX     0x00100000      /* only half duplex */
 #define SNDRV_PCM_INFO_JOINT_DUPLEX    0x00200000      /* playback and capture 
stream are somewhat correlated */
 #define SNDRV_PCM_INFO_SYNC_START      0x00400000      /* pcm support some 
kind of sync go */
+#define SNDRV_PCM_INFO_NO_PERIOD_WAKEUP        0x00800000      /* period 
wakeup can be disabled */
 #define SNDRV_PCM_INFO_FIFO_IN_FRAMES  0x80000000      /* internal kernel flag 
- FIFO size is in frames */
 
 typedef int __bitwise snd_pcm_state_t;
@@ -334,6 +335,8 @@ 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_EXPORT_BUFFER      (1<<1)  /* export buffer */
+#define SNDRV_PCM_HW_PARAMS_NO_PERIOD_WAKEUP   (1<<2)  /* disable period 
wakeups */
 
 struct snd_interval {
        unsigned int min, max;
diff --git a/include/sound/pcm.h b/include/sound/pcm.h
index dfd9b76..e731f8d 100644
--- a/include/sound/pcm.h
+++ b/include/sound/pcm.h
@@ -297,6 +297,7 @@ struct snd_pcm_runtime {
        unsigned int info;
        unsigned int rate_num;
        unsigned int rate_den;
+       unsigned int no_period_wakeup: 1;
 
        /* -- SW params -- */
        int tstamp_mode;                /* mmap timestamp is updated */
diff --git a/sound/core/pcm_lib.c b/sound/core/pcm_lib.c
index 60937c7..640dcfe 100644
--- a/sound/core/pcm_lib.c
+++ b/sound/core/pcm_lib.c
@@ -378,6 +378,29 @@ static int snd_pcm_update_hw_ptr0(struct snd_pcm_substream 
*substream,
                           (unsigned long)new_hw_ptr,
                           (unsigned long)runtime->hw_ptr_base);
        }
+
+       if (runtime->no_period_wakeup) {
+               snd_pcm_sframes_t xrun_threshold;
+               /*
+                * Without regular period interrupts, we have to check
+                * the elapsed time to detect xruns.
+                */
+               jdelta = jiffies - runtime->hw_ptr_jiffies;
+               if (jdelta < runtime->hw_ptr_buffer_jiffies / 2)
+                       goto no_delta_check;
+               hdelta = jdelta - delta * HZ / runtime->rate;
+               xrun_threshold = runtime->hw_ptr_buffer_jiffies / 2 + 1;
+               while (hdelta > xrun_threshold) {
+                       delta += runtime->buffer_size;
+                       hw_base += runtime->buffer_size;
+                       if (hw_base >= runtime->boundary)
+                               hw_base = 0;
+                       new_hw_ptr = hw_base + pos;
+                       hdelta -= runtime->hw_ptr_buffer_jiffies;
+               }
+               goto no_delta_check;
+       }
+
        /* something must be really wrong */
        if (delta >= runtime->buffer_size + runtime->period_size) {
                hw_ptr_error(substream,
@@ -447,6 +470,7 @@ static int snd_pcm_update_hw_ptr0(struct snd_pcm_substream 
*substream,
                             (long)old_hw_ptr);
        }
 
+no_delta_check:
        if (runtime->status->hw_ptr == new_hw_ptr) {
                trace_snd_pointer(substream, __func__,
                                  __builtin_return_address(0),
@@ -1741,8 +1765,18 @@ static int wait_for_avail(struct snd_pcm_substream 
*substream,
        wait_queue_t wait;
        int err = 0;
        snd_pcm_uframes_t avail = 0;
-       long tout;
-
+       long wait_time, tout;
+
+       if (runtime->no_period_wakeup)
+               wait_time = MAX_SCHEDULE_TIMEOUT;
+       else {
+               wait_time = 10;
+               if (runtime->rate) {
+                       long t = runtime->period_size * 2 / runtime->rate;
+                       wait_time = max(t, wait_time);
+               }
+               wait_time = msecs_to_jiffies(wait_time * 1000);
+       }
        init_waitqueue_entry(&wait, current);
        add_wait_queue(&runtime->tsleep, &wait);
        for (;;) {
@@ -1750,9 +1784,8 @@ static int wait_for_avail(struct snd_pcm_substream 
*substream,
                        err = -ERESTARTSYS;
                        break;
                }
-               set_current_state(TASK_INTERRUPTIBLE);
                snd_pcm_stream_unlock_irq(substream);
-               tout = schedule_timeout(msecs_to_jiffies(10000));
+               tout = schedule_timeout_interruptible(wait_time);
                snd_pcm_stream_lock_irq(substream);
                switch (runtime->status->state) {
                case SNDRV_PCM_STATE_SUSPENDED:
diff --git a/sound/core/pcm_native.c b/sound/core/pcm_native.c
index 1c396ba..ad161bd 100644
--- a/sound/core/pcm_native.c
+++ b/sound/core/pcm_native.c
@@ -425,6 +425,9 @@ static int snd_pcm_hw_params(struct snd_pcm_substream 
*substream,
        runtime->info = params->info;
        runtime->rate_num = params->rate_num;
        runtime->rate_den = params->rate_den;
+       runtime->no_period_wakeup =
+                       (params->info & SNDRV_PCM_INFO_NO_PERIOD_WAKEUP) &&
+                       (params->flags & SNDRV_PCM_HW_PARAMS_NO_PERIOD_WAKEUP);
 
        bits = snd_pcm_format_physical_width(runtime->format);
        runtime->sample_bits = bits;
@@ -1490,11 +1493,20 @@ static int snd_pcm_drain(struct snd_pcm_substream 
*substream,
                        break; /* all drained */
                init_waitqueue_entry(&wait, current);
                add_wait_queue(&to_check->sleep, &wait);
-               set_current_state(TASK_INTERRUPTIBLE);
                snd_pcm_stream_unlock_irq(substream);
                up_read(&snd_pcm_link_rwsem);
                snd_power_unlock(card);
-               tout = schedule_timeout(10 * HZ);
+               if (runtime->no_period_wakeup)
+                       tout = MAX_SCHEDULE_TIMEOUT;
+               else {
+                       tout = 10;
+                       if (runtime->rate) {
+                               long t = runtime->period_size * 2 / 
runtime->rate;
+                               tout = max(t, tout);
+                       }
+                       tout = msecs_to_jiffies(tout * 1000);
+               }
+               tout = schedule_timeout_interruptible(tout);
                snd_power_lock(card);
                down_read(&snd_pcm_link_rwsem);
                snd_pcm_stream_lock_irq(substream);
-- 
1.7.1
 
_______________________________________________
MeeGo-kernel mailing list
[email protected]
http://lists.meego.com/listinfo/meego-kernel

Reply via email to