ALSA: dice: dynamic sample rate selection
authorClemens Ladisch <clemens@ladisch.de>
Sun, 4 Dec 2011 21:07:01 +0000 (22:07 +0100)
committerClemens Ladisch <clemens@ladisch.de>
Sun, 20 Oct 2013 20:07:57 +0000 (22:07 +0200)
Instead of relying of some control panel application to configure some
fixed sample rate, allow applications to set it automatically.

Signed-off-by: Clemens Ladisch <clemens@ladisch.de>
sound/firewire/dice.c

index e6bba6d32bd880dcd53362b697b1b2c60f5cf38c..61dd00c4fae3f2be962ffd22998dc784d0193a02 100644 (file)
@@ -70,6 +70,17 @@ static const unsigned int dice_rates[] = {
        [6] = 192000,
 };
 
+static unsigned int rate_to_index(unsigned int rate)
+{
+       unsigned int i;
+
+       for (i = 0; i < ARRAY_SIZE(dice_rates); ++i)
+               if (dice_rates[i] == rate)
+                       return i;
+
+       return 0;
+}
+
 static unsigned int rate_index_to_mode(unsigned int rate_index)
 {
        return ((int)rate_index - 1) / 2;
@@ -302,6 +313,59 @@ static void dice_notification(struct fw_card *card, struct fw_request *request,
        wake_up(&dice->hwdep_wait);
 }
 
+static int dice_rate_constraint(struct snd_pcm_hw_params *params,
+                               struct snd_pcm_hw_rule *rule)
+{
+       struct dice *dice = rule->private;
+       const struct snd_interval *channels =
+               hw_param_interval_c(params, SNDRV_PCM_HW_PARAM_CHANNELS);
+       struct snd_interval *rate =
+               hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE);
+       struct snd_interval allowed_rates = {
+               .min = UINT_MAX, .max = 0, .integer = 1
+       };
+       unsigned int i, mode;
+
+       for (i = 0; i < ARRAY_SIZE(dice_rates); ++i) {
+               mode = rate_index_to_mode(i);
+               if ((dice->clock_caps & (1 << i)) &&
+                   snd_interval_test(channels, dice->rx_channels[mode])) {
+                       allowed_rates.min = min(allowed_rates.min,
+                                               dice_rates[i]);
+                       allowed_rates.max = max(allowed_rates.max,
+                                               dice_rates[i]);
+               }
+       }
+
+       return snd_interval_refine(rate, &allowed_rates);
+}
+
+static int dice_channels_constraint(struct snd_pcm_hw_params *params,
+                                   struct snd_pcm_hw_rule *rule)
+{
+       struct dice *dice = rule->private;
+       const struct snd_interval *rate =
+               hw_param_interval_c(params, SNDRV_PCM_HW_PARAM_RATE);
+       struct snd_interval *channels =
+               hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS);
+       struct snd_interval allowed_channels = {
+               .min = UINT_MAX, .max = 0, .integer = 1
+       };
+       unsigned int i, mode;
+
+       for (i = 0; i < ARRAY_SIZE(dice_rates); ++i)
+               if ((dice->clock_caps & (1 << i)) &&
+                   snd_interval_test(rate, dice_rates[i])) {
+                       mode = rate_index_to_mode(i);
+                       allowed_channels.min = min(allowed_channels.min,
+                                                  dice->rx_channels[mode]);
+                       allowed_channels.max = max(allowed_channels.max,
+                                                  dice->rx_channels[mode]);
+               }
+
+       return snd_interval_refine(channels, &allowed_channels);
+}
+
 static int dice_open(struct snd_pcm_substream *substream)
 {
        static const struct snd_pcm_hardware hardware = {
@@ -311,6 +375,8 @@ static int dice_open(struct snd_pcm_substream *substream)
                        SNDRV_PCM_INFO_INTERLEAVED |
                        SNDRV_PCM_INFO_BLOCK_TRANSFER,
                .formats = AMDTP_OUT_PCM_FORMAT_BITS,
+               .channels_min = UINT_MAX,
+               .channels_max = 0,
                .buffer_bytes_max = 16 * 1024 * 1024,
                .period_bytes_min = 1,
                .period_bytes_max = UINT_MAX,
@@ -319,53 +385,46 @@ static int dice_open(struct snd_pcm_substream *substream)
        };
        struct dice *dice = substream->private_data;
        struct snd_pcm_runtime *runtime = substream->runtime;
-       __be32 clock_sel, data[2];
-       unsigned int rate_index, number_audio, number_midi;
+       unsigned int i;
        int err;
 
        err = dice_try_lock(dice);
        if (err < 0)
                goto error;
 
-       err = snd_fw_transaction(dice->unit, TCODE_READ_QUADLET_REQUEST,
-                                global_address(dice, GLOBAL_CLOCK_SELECT),
-                                &clock_sel, 4, 0);
-       if (err < 0)
-               goto err_lock;
-       rate_index = (be32_to_cpu(clock_sel) & CLOCK_RATE_MASK)
-                                                       >> CLOCK_RATE_SHIFT;
-       if (rate_index >= ARRAY_SIZE(dice_rates)) {
-               err = -ENXIO;
-               goto err_lock;
-       }
-
-       err = snd_fw_transaction(dice->unit, TCODE_READ_BLOCK_REQUEST,
-                                rx_address(dice, RX_NUMBER_AUDIO),
-                                data, 2 * 4, 0);
-       if (err < 0)
-               goto err_lock;
-       number_audio = be32_to_cpu(data[0]);
-       number_midi = be32_to_cpu(data[1]);
-
        runtime->hw = hardware;
 
-       runtime->hw.rates = snd_pcm_rate_to_rate_bit(dice_rates[rate_index]);
+       for (i = 0; i < ARRAY_SIZE(dice_rates); ++i)
+               if (dice->clock_caps & (1 << i))
+                       runtime->hw.rates |=
+                               snd_pcm_rate_to_rate_bit(dice_rates[i]);
        snd_pcm_limit_hw_rates(runtime);
 
-       runtime->hw.channels_min = number_audio;
-       runtime->hw.channels_max = number_audio;
+       for (i = 0; i < 3; ++i)
+               if (dice->rx_channels[i]) {
+                       runtime->hw.channels_min = min(runtime->hw.channels_min,
+                                                      dice->rx_channels[i]);
+                       runtime->hw.channels_max = max(runtime->hw.channels_max,
+                                                      dice->rx_channels[i]);
+               }
 
-       amdtp_out_stream_set_parameters(&dice->stream, dice_rates[rate_index],
-                                       number_audio, number_midi);
+       err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_RATE,
+                                 dice_rate_constraint, dice,
+                                 SNDRV_PCM_HW_PARAM_CHANNELS, -1);
+       if (err < 0)
+               goto err_lock;
+       err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS,
+                                 dice_channels_constraint, dice,
+                                 SNDRV_PCM_HW_PARAM_RATE, -1);
+       if (err < 0)
+               goto err_lock;
 
        err = snd_pcm_hw_constraint_step(runtime, 0,
-                                        SNDRV_PCM_HW_PARAM_PERIOD_SIZE,
-                                        amdtp_syt_intervals[rate_index]);
+                                        SNDRV_PCM_HW_PARAM_PERIOD_SIZE, 32);
        if (err < 0)
                goto err_lock;
        err = snd_pcm_hw_constraint_step(runtime, 0,
-                                        SNDRV_PCM_HW_PARAM_BUFFER_SIZE,
-                                        amdtp_syt_intervals[rate_index]);
+                                        SNDRV_PCM_HW_PARAM_BUFFER_SIZE, 32);
        if (err < 0)
                goto err_lock;
 
@@ -502,6 +561,7 @@ static int dice_hw_params(struct snd_pcm_substream *substream,
                          struct snd_pcm_hw_params *hw_params)
 {
        struct dice *dice = substream->private_data;
+       unsigned int rate_index, mode;
        int err;
 
        mutex_lock(&dice->mutex);
@@ -511,15 +571,22 @@ static int dice_hw_params(struct snd_pcm_substream *substream,
        err = snd_pcm_lib_alloc_vmalloc_buffer(substream,
                                               params_buffer_bytes(hw_params));
        if (err < 0)
-               goto error;
+               return err;
 
+       rate_index = rate_to_index(params_rate(hw_params));
+       err = dice_change_rate(dice, rate_index << CLOCK_RATE_SHIFT);
+       if (err < 0)
+               return err;
+
+       mode = rate_index_to_mode(rate_index);
+       amdtp_out_stream_set_parameters(&dice->stream,
+                                       params_rate(hw_params),
+                                       params_channels(hw_params),
+                                       dice->rx_midi_ports[mode]);
        amdtp_out_stream_set_pcm_format(&dice->stream,
                                        params_format(hw_params));
 
        return 0;
-
-error:
-       return err;
 }
 
 static int dice_hw_free(struct snd_pcm_substream *substream)