Commit 4edeb831 authored by Clemens Ladisch's avatar Clemens Ladisch

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: default avatarClemens Ladisch <clemens@ladisch.de>
parent 15a75c8b
...@@ -70,6 +70,17 @@ static const unsigned int dice_rates[] = { ...@@ -70,6 +70,17 @@ static const unsigned int dice_rates[] = {
[6] = 192000, [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) static unsigned int rate_index_to_mode(unsigned int rate_index)
{ {
return ((int)rate_index - 1) / 2; return ((int)rate_index - 1) / 2;
...@@ -302,6 +313,59 @@ static void dice_notification(struct fw_card *card, struct fw_request *request, ...@@ -302,6 +313,59 @@ static void dice_notification(struct fw_card *card, struct fw_request *request,
wake_up(&dice->hwdep_wait); 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 int dice_open(struct snd_pcm_substream *substream)
{ {
static const struct snd_pcm_hardware hardware = { static const struct snd_pcm_hardware hardware = {
...@@ -311,6 +375,8 @@ static int dice_open(struct snd_pcm_substream *substream) ...@@ -311,6 +375,8 @@ static int dice_open(struct snd_pcm_substream *substream)
SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_INTERLEAVED |
SNDRV_PCM_INFO_BLOCK_TRANSFER, SNDRV_PCM_INFO_BLOCK_TRANSFER,
.formats = AMDTP_OUT_PCM_FORMAT_BITS, .formats = AMDTP_OUT_PCM_FORMAT_BITS,
.channels_min = UINT_MAX,
.channels_max = 0,
.buffer_bytes_max = 16 * 1024 * 1024, .buffer_bytes_max = 16 * 1024 * 1024,
.period_bytes_min = 1, .period_bytes_min = 1,
.period_bytes_max = UINT_MAX, .period_bytes_max = UINT_MAX,
...@@ -319,53 +385,46 @@ static int dice_open(struct snd_pcm_substream *substream) ...@@ -319,53 +385,46 @@ static int dice_open(struct snd_pcm_substream *substream)
}; };
struct dice *dice = substream->private_data; struct dice *dice = substream->private_data;
struct snd_pcm_runtime *runtime = substream->runtime; struct snd_pcm_runtime *runtime = substream->runtime;
__be32 clock_sel, data[2]; unsigned int i;
unsigned int rate_index, number_audio, number_midi;
int err; int err;
err = dice_try_lock(dice); err = dice_try_lock(dice);
if (err < 0) if (err < 0)
goto error; 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 = 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); snd_pcm_limit_hw_rates(runtime);
runtime->hw.channels_min = number_audio; for (i = 0; i < 3; ++i)
runtime->hw.channels_max = number_audio; 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], err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_RATE,
number_audio, number_midi); 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, err = snd_pcm_hw_constraint_step(runtime, 0,
SNDRV_PCM_HW_PARAM_PERIOD_SIZE, SNDRV_PCM_HW_PARAM_PERIOD_SIZE, 32);
amdtp_syt_intervals[rate_index]);
if (err < 0) if (err < 0)
goto err_lock; goto err_lock;
err = snd_pcm_hw_constraint_step(runtime, 0, err = snd_pcm_hw_constraint_step(runtime, 0,
SNDRV_PCM_HW_PARAM_BUFFER_SIZE, SNDRV_PCM_HW_PARAM_BUFFER_SIZE, 32);
amdtp_syt_intervals[rate_index]);
if (err < 0) if (err < 0)
goto err_lock; goto err_lock;
...@@ -502,6 +561,7 @@ static int dice_hw_params(struct snd_pcm_substream *substream, ...@@ -502,6 +561,7 @@ static int dice_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *hw_params) struct snd_pcm_hw_params *hw_params)
{ {
struct dice *dice = substream->private_data; struct dice *dice = substream->private_data;
unsigned int rate_index, mode;
int err; int err;
mutex_lock(&dice->mutex); mutex_lock(&dice->mutex);
...@@ -511,15 +571,22 @@ static int dice_hw_params(struct snd_pcm_substream *substream, ...@@ -511,15 +571,22 @@ static int dice_hw_params(struct snd_pcm_substream *substream,
err = snd_pcm_lib_alloc_vmalloc_buffer(substream, err = snd_pcm_lib_alloc_vmalloc_buffer(substream,
params_buffer_bytes(hw_params)); params_buffer_bytes(hw_params));
if (err < 0) 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, amdtp_out_stream_set_pcm_format(&dice->stream,
params_format(hw_params)); params_format(hw_params));
return 0; return 0;
error:
return err;
} }
static int dice_hw_free(struct snd_pcm_substream *substream) static int dice_hw_free(struct snd_pcm_substream *substream)
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment