Merge branch 'stable-3.2' into pandora-3.2
[pandora-kernel.git] / sound / drivers / aloop.c
index 42b876d..4e01b02 100644 (file)
@@ -39,6 +39,7 @@
 #include <sound/core.h>
 #include <sound/control.h>
 #include <sound/pcm.h>
+#include <sound/pcm_params.h>
 #include <sound/info.h>
 #include <sound/initval.h>
 
@@ -194,6 +195,11 @@ static inline void loopback_timer_stop(struct loopback_pcm *dpcm)
        spin_unlock(&dpcm->timer_lock);
 }
 
+static inline void loopback_timer_stop_sync(struct loopback_pcm *dpcm)
+{
+       del_timer_sync(&dpcm->timer);
+}
+
 #define CABLE_VALID_PLAYBACK   (1 << SNDRV_PCM_STREAM_PLAYBACK)
 #define CABLE_VALID_CAPTURE    (1 << SNDRV_PCM_STREAM_CAPTURE)
 #define CABLE_VALID_BOTH       (CABLE_VALID_PLAYBACK|CABLE_VALID_CAPTURE)
@@ -307,19 +313,6 @@ static int loopback_trigger(struct snd_pcm_substream *substream, int cmd)
        return 0;
 }
 
-static void params_change_substream(struct loopback_pcm *dpcm,
-                                   struct snd_pcm_runtime *runtime)
-{
-       struct snd_pcm_runtime *dst_runtime;
-
-       if (dpcm == NULL || dpcm->substream == NULL)
-               return;
-       dst_runtime = dpcm->substream->runtime;
-       if (dst_runtime == NULL)
-               return;
-       dst_runtime->hw = dpcm->cable->hw;
-}
-
 static void params_change(struct snd_pcm_substream *substream)
 {
        struct snd_pcm_runtime *runtime = substream->runtime;
@@ -331,10 +324,6 @@ static void params_change(struct snd_pcm_substream *substream)
        cable->hw.rate_max = runtime->rate;
        cable->hw.channels_min = runtime->channels;
        cable->hw.channels_max = runtime->channels;
-       params_change_substream(cable->streams[SNDRV_PCM_STREAM_PLAYBACK],
-                               runtime);
-       params_change_substream(cable->streams[SNDRV_PCM_STREAM_CAPTURE],
-                               runtime);
 }
 
 static int loopback_prepare(struct snd_pcm_substream *substream)
@@ -344,6 +333,8 @@ static int loopback_prepare(struct snd_pcm_substream *substream)
        struct loopback_cable *cable = dpcm->cable;
        int bps, salign;
 
+       loopback_timer_stop_sync(dpcm);
+
        salign = (snd_pcm_format_width(runtime->format) *
                                                runtime->channels) / 8;
        bps = salign * runtime->rate;
@@ -610,26 +601,29 @@ static unsigned int get_cable_index(struct snd_pcm_substream *substream)
 static int rule_format(struct snd_pcm_hw_params *params,
                       struct snd_pcm_hw_rule *rule)
 {
+       struct loopback_pcm *dpcm = rule->private;
+       struct loopback_cable *cable = dpcm->cable;
+       struct snd_mask m;
 
-       struct snd_pcm_hardware *hw = rule->private;
-       struct snd_mask *maskp = hw_param_mask(params, rule->var);
-
-       maskp->bits[0] &= (u_int32_t)hw->formats;
-       maskp->bits[1] &= (u_int32_t)(hw->formats >> 32);
-       memset(maskp->bits + 2, 0, (SNDRV_MASK_MAX-64) / 8); /* clear rest */
-       if (! maskp->bits[0] && ! maskp->bits[1])
-               return -EINVAL;
-       return 0;
+       snd_mask_none(&m);
+       mutex_lock(&dpcm->loopback->cable_lock);
+       m.bits[0] = (u_int32_t)cable->hw.formats;
+       m.bits[1] = (u_int32_t)(cable->hw.formats >> 32);
+       mutex_unlock(&dpcm->loopback->cable_lock);
+       return snd_mask_refine(hw_param_mask(params, rule->var), &m);
 }
 
 static int rule_rate(struct snd_pcm_hw_params *params,
                     struct snd_pcm_hw_rule *rule)
 {
-       struct snd_pcm_hardware *hw = rule->private;
+       struct loopback_pcm *dpcm = rule->private;
+       struct loopback_cable *cable = dpcm->cable;
        struct snd_interval t;
 
-        t.min = hw->rate_min;
-        t.max = hw->rate_max;
+       mutex_lock(&dpcm->loopback->cable_lock);
+       t.min = cable->hw.rate_min;
+       t.max = cable->hw.rate_max;
+       mutex_unlock(&dpcm->loopback->cable_lock);
         t.openmin = t.openmax = 0;
         t.integer = 0;
        return snd_interval_refine(hw_param_interval(params, rule->var), &t);
@@ -638,22 +632,46 @@ static int rule_rate(struct snd_pcm_hw_params *params,
 static int rule_channels(struct snd_pcm_hw_params *params,
                         struct snd_pcm_hw_rule *rule)
 {
-       struct snd_pcm_hardware *hw = rule->private;
+       struct loopback_pcm *dpcm = rule->private;
+       struct loopback_cable *cable = dpcm->cable;
        struct snd_interval t;
 
-        t.min = hw->channels_min;
-        t.max = hw->channels_max;
+       mutex_lock(&dpcm->loopback->cable_lock);
+       t.min = cable->hw.channels_min;
+       t.max = cable->hw.channels_max;
+       mutex_unlock(&dpcm->loopback->cable_lock);
         t.openmin = t.openmax = 0;
         t.integer = 0;
        return snd_interval_refine(hw_param_interval(params, rule->var), &t);
 }
 
+static void free_cable(struct snd_pcm_substream *substream)
+{
+       struct loopback *loopback = substream->private_data;
+       int dev = get_cable_index(substream);
+       struct loopback_cable *cable;
+
+       cable = loopback->cables[substream->number][dev];
+       if (!cable)
+               return;
+       if (cable->streams[!substream->stream]) {
+               /* other stream is still alive */
+               spin_lock_irq(&cable->lock);
+               cable->streams[substream->stream] = NULL;
+               spin_unlock_irq(&cable->lock);
+       } else {
+               /* free the cable */
+               loopback->cables[substream->number][dev] = NULL;
+               kfree(cable);
+       }
+}
+
 static int loopback_open(struct snd_pcm_substream *substream)
 {
        struct snd_pcm_runtime *runtime = substream->runtime;
        struct loopback *loopback = substream->private_data;
        struct loopback_pcm *dpcm;
-       struct loopback_cable *cable;
+       struct loopback_cable *cable = NULL;
        int err = 0;
        int dev = get_cable_index(substream);
 
@@ -673,7 +691,6 @@ static int loopback_open(struct snd_pcm_substream *substream)
        if (!cable) {
                cable = kzalloc(sizeof(*cable), GFP_KERNEL);
                if (!cable) {
-                       kfree(dpcm);
                        err = -ENOMEM;
                        goto unlock;
                }
@@ -682,7 +699,6 @@ static int loopback_open(struct snd_pcm_substream *substream)
                loopback->cables[substream->number][dev] = cable;
        }
        dpcm->cable = cable;
-       cable->streams[substream->stream] = dpcm;
 
        snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS);
 
@@ -691,19 +707,19 @@ static int loopback_open(struct snd_pcm_substream *substream)
        /* are cached -> they do not reflect the actual state */
        err = snd_pcm_hw_rule_add(runtime, 0,
                                  SNDRV_PCM_HW_PARAM_FORMAT,
-                                 rule_format, &runtime->hw,
+                                 rule_format, dpcm,
                                  SNDRV_PCM_HW_PARAM_FORMAT, -1);
        if (err < 0)
                goto unlock;
        err = snd_pcm_hw_rule_add(runtime, 0,
                                  SNDRV_PCM_HW_PARAM_RATE,
-                                 rule_rate, &runtime->hw,
+                                 rule_rate, dpcm,
                                  SNDRV_PCM_HW_PARAM_RATE, -1);
        if (err < 0)
                goto unlock;
        err = snd_pcm_hw_rule_add(runtime, 0,
                                  SNDRV_PCM_HW_PARAM_CHANNELS,
-                                 rule_channels, &runtime->hw,
+                                 rule_channels, dpcm,
                                  SNDRV_PCM_HW_PARAM_CHANNELS, -1);
        if (err < 0)
                goto unlock;
@@ -714,7 +730,16 @@ static int loopback_open(struct snd_pcm_substream *substream)
                runtime->hw = loopback_pcm_hardware;
        else
                runtime->hw = cable->hw;
+
+       spin_lock_irq(&cable->lock);
+       cable->streams[substream->stream] = dpcm;
+       spin_unlock_irq(&cable->lock);
+
  unlock:
+       if (err < 0) {
+               free_cable(substream);
+               kfree(dpcm);
+       }
        mutex_unlock(&loopback->cable_lock);
        return err;
 }
@@ -723,20 +748,10 @@ static int loopback_close(struct snd_pcm_substream *substream)
 {
        struct loopback *loopback = substream->private_data;
        struct loopback_pcm *dpcm = substream->runtime->private_data;
-       struct loopback_cable *cable;
-       int dev = get_cable_index(substream);
 
-       loopback_timer_stop(dpcm);
+       loopback_timer_stop_sync(dpcm);
        mutex_lock(&loopback->cable_lock);
-       cable = loopback->cables[substream->number][dev];
-       if (cable->streams[!substream->stream]) {
-               /* other stream is still alive */
-               cable->streams[substream->stream] = NULL;
-       } else {
-               /* free the cable */
-               loopback->cables[substream->number][dev] = NULL;
-               kfree(cable);
-       }
+       free_cable(substream);
        mutex_unlock(&loopback->cable_lock);
        return 0;
 }