ASoC: TWL4030: Fix for the constraint handling
authorPeter Ujfalusi <peter.ujfalusi@nokia.com>
Fri, 17 Apr 2009 12:55:08 +0000 (15:55 +0300)
committerMark Brown <broonie@opensource.wolfsonmicro.com>
Fri, 17 Apr 2009 15:02:23 +0000 (16:02 +0100)
The original implementation of the constraints were good against sane
applications.
If the opening sequence is:
stream1_open, stream1_hw_params, stream2_open, stream2_hw_params -> the
constraints are set correctly for stream2.

But if the sequence is:
stream1_open, stream2_open, stream2_hw_params, stream1_hw_params -> than stream2
would receive constraint rate = 0, sample_bits = 0, since the stream1 has not
yet called hw_params...

The command to trigger this event:
gst-launch-0.10 alsasrc device=hw:0 ! alsasink device=hw:0 sync=false

This patch does some 'black magic' in order to always set the correct
constraints and sets it only when it is needed for the other stream.

Signed-off-by: Peter Ujfalusi <peter.ujfalusi@nokia.com>
Signed-off-by: Mark Brown <broonie@opensource.wolfsonmicro.com>
sound/soc/codecs/twl4030.c

index 921b205de28ab51ef5239dd7e07fe01cfaac2066..a1b76d7fd1300adf37c6c7a39cfa8e48312998f2 100644 (file)
@@ -125,6 +125,11 @@ struct twl4030_priv {
 
        struct snd_pcm_substream *master_substream;
        struct snd_pcm_substream *slave_substream;
+
+       unsigned int configured;
+       unsigned int rate;
+       unsigned int sample_bits;
+       unsigned int channels;
 };
 
 /*
@@ -1220,6 +1225,36 @@ static int twl4030_set_bias_level(struct snd_soc_codec *codec,
        return 0;
 }
 
+static void twl4030_constraints(struct twl4030_priv *twl4030,
+                               struct snd_pcm_substream *mst_substream)
+{
+       struct snd_pcm_substream *slv_substream;
+
+       /* Pick the stream, which need to be constrained */
+       if (mst_substream == twl4030->master_substream)
+               slv_substream = twl4030->slave_substream;
+       else if (mst_substream == twl4030->slave_substream)
+               slv_substream = twl4030->master_substream;
+       else /* This should not happen.. */
+               return;
+
+       /* Set the constraints according to the already configured stream */
+       snd_pcm_hw_constraint_minmax(slv_substream->runtime,
+                               SNDRV_PCM_HW_PARAM_RATE,
+                               twl4030->rate,
+                               twl4030->rate);
+
+       snd_pcm_hw_constraint_minmax(slv_substream->runtime,
+                               SNDRV_PCM_HW_PARAM_SAMPLE_BITS,
+                               twl4030->sample_bits,
+                               twl4030->sample_bits);
+
+       snd_pcm_hw_constraint_minmax(slv_substream->runtime,
+                               SNDRV_PCM_HW_PARAM_CHANNELS,
+                               twl4030->channels,
+                               twl4030->channels);
+}
+
 static int twl4030_startup(struct snd_pcm_substream *substream,
                           struct snd_soc_dai *dai)
 {
@@ -1228,26 +1263,16 @@ static int twl4030_startup(struct snd_pcm_substream *substream,
        struct snd_soc_codec *codec = socdev->card->codec;
        struct twl4030_priv *twl4030 = codec->private_data;
 
-       /* If we already have a playback or capture going then constrain
-        * this substream to match it.
-        */
        if (twl4030->master_substream) {
-               struct snd_pcm_runtime *master_runtime;
-               master_runtime = twl4030->master_substream->runtime;
-
-               snd_pcm_hw_constraint_minmax(substream->runtime,
-                                            SNDRV_PCM_HW_PARAM_RATE,
-                                            master_runtime->rate,
-                                            master_runtime->rate);
-
-               snd_pcm_hw_constraint_minmax(substream->runtime,
-                                            SNDRV_PCM_HW_PARAM_SAMPLE_BITS,
-                                            master_runtime->sample_bits,
-                                            master_runtime->sample_bits);
-
                twl4030->slave_substream = substream;
-       } else
+               /* The DAI has one configuration for playback and capture, so
+                * if the DAI has been already configured then constrain this
+                * substream to match it. */
+               if (twl4030->configured)
+                       twl4030_constraints(twl4030, twl4030->master_substream);
+       } else {
                twl4030->master_substream = substream;
+       }
 
        return 0;
 }
@@ -1264,6 +1289,13 @@ static void twl4030_shutdown(struct snd_pcm_substream *substream,
                twl4030->master_substream = twl4030->slave_substream;
 
        twl4030->slave_substream = NULL;
+
+       /* If all streams are closed, or the remaining stream has not yet
+        * been configured than set the DAI as not configured. */
+       if (!twl4030->master_substream)
+               twl4030->configured = 0;
+        else if (!twl4030->master_substream->runtime->channels)
+               twl4030->configured = 0;
 }
 
 static int twl4030_hw_params(struct snd_pcm_substream *substream,
@@ -1276,8 +1308,8 @@ static int twl4030_hw_params(struct snd_pcm_substream *substream,
        struct twl4030_priv *twl4030 = codec->private_data;
        u8 mode, old_mode, format, old_format;
 
-       if (substream == twl4030->slave_substream)
-               /* Ignoring hw_params for slave substream */
+       if (twl4030->configured)
+               /* Ignoring hw_params for already configured DAI */
                return 0;
 
        /* bit rate */
@@ -1357,6 +1389,21 @@ static int twl4030_hw_params(struct snd_pcm_substream *substream,
                /* set CODECPDZ afterwards */
                twl4030_codec_enable(codec, 1);
        }
+
+       /* Store the important parameters for the DAI configuration and set
+        * the DAI as configured */
+       twl4030->configured = 1;
+       twl4030->rate = params_rate(params);
+       twl4030->sample_bits = hw_param_interval(params,
+                                       SNDRV_PCM_HW_PARAM_SAMPLE_BITS)->min;
+       twl4030->channels = params_channels(params);
+
+       /* If both playback and capture streams are open, and one of them
+        * is setting the hw parameters right now (since we are here), set
+        * constraints to the other stream to match the current one. */
+       if (twl4030->slave_substream)
+               twl4030_constraints(twl4030, substream);
+
        return 0;
 }