summaryrefslogtreecommitdiffstats
path: root/sound/firewire
diff options
context:
space:
mode:
authorClemens Ladisch <clemens@ladisch.de>2011-12-04 22:07:01 +0100
committerClemens Ladisch <clemens@ladisch.de>2013-10-20 22:07:57 +0200
commit4edeb831f32d17fba056eb752f7afc26a19674a0 (patch)
tree5573fb9615f009890bbbc6dce1e1ae713b826d74 /sound/firewire
parentALSA: dice: get rate-dependent parameters (diff)
downloadlinux-4edeb831f32d17fba056eb752f7afc26a19674a0.tar.xz
linux-4edeb831f32d17fba056eb752f7afc26a19674a0.zip
ALSA: dice: dynamic sample rate selection
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>
Diffstat (limited to 'sound/firewire')
-rw-r--r--sound/firewire/dice.c137
1 files changed, 102 insertions, 35 deletions
diff --git a/sound/firewire/dice.c b/sound/firewire/dice.c
index e6bba6d32bd8..61dd00c4fae3 100644
--- a/sound/firewire/dice.c
+++ b/sound/firewire/dice.c
@@ -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)