Skip to content

Commit

Permalink
ALSA: dice: dynamic sample rate selection
Browse files Browse the repository at this point in the history
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 <[email protected]>
  • Loading branch information
cladisch committed Oct 20, 2013
1 parent 15a75c8 commit 4edeb83
Showing 1 changed file with 102 additions and 35 deletions.
137 changes: 102 additions & 35 deletions sound/firewire/dice.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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 = {
Expand All @@ -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,
Expand All @@ -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;

Expand Down Expand Up @@ -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);
Expand All @@ -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)
Expand Down

0 comments on commit 4edeb83

Please sign in to comment.