mirror of
https://github.com/torvalds/linux.git
synced 2024-11-23 20:51:44 +00:00
ALSA: dice: limit to current sampling transfer frequency
ALSA PCM core has a functionality for rule of PCM substream parameters. Typically, when userspace opens PCM character devices, each driver adds its own rules to PCM substream according to design of hardware. When the userspace executes hw_params ioctl with favorite parameters, the actual parameters are calculated according to the rules and the given parameters. Then, the result is returned to userspace. Currently, ALSA Dice driver has the rule between channels and rates, while Dice interface design doesn't allow drivers to retrieve all of the combinations. Dice drivers are just allowed to get current sampling transfer frequency and the number of multi bit linear audio data channels in an data block of an AMDTP packet. This commit purges the rule, and limit PCM substreams to current sampling transfer frequency, following to the interface design. Signed-off-by: Takashi Sakamoto <o-takashi@sakamocchi.jp> Signed-off-by: Takashi Iwai <tiwai@suse.de>
This commit is contained in:
parent
efd931d6c7
commit
0d5ee195b1
@ -9,99 +9,40 @@
|
||||
|
||||
#include "dice.h"
|
||||
|
||||
static int dice_rate_constraint(struct snd_pcm_hw_params *params,
|
||||
struct snd_pcm_hw_rule *rule)
|
||||
{
|
||||
struct snd_pcm_substream *substream = rule->private;
|
||||
struct snd_dice *dice = substream->private_data;
|
||||
|
||||
const struct snd_interval *c =
|
||||
hw_param_interval_c(params, SNDRV_PCM_HW_PARAM_CHANNELS);
|
||||
struct snd_interval *r =
|
||||
hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE);
|
||||
struct snd_interval rates = {
|
||||
.min = UINT_MAX, .max = 0, .integer = 1
|
||||
};
|
||||
unsigned int i, rate, mode, *pcm_channels;
|
||||
|
||||
if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
|
||||
pcm_channels = dice->tx_channels;
|
||||
else
|
||||
pcm_channels = dice->rx_channels;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(snd_dice_rates); ++i) {
|
||||
rate = snd_dice_rates[i];
|
||||
if (snd_dice_stream_get_rate_mode(dice, rate, &mode) < 0)
|
||||
continue;
|
||||
|
||||
if (!snd_interval_test(c, pcm_channels[mode]))
|
||||
continue;
|
||||
|
||||
rates.min = min(rates.min, rate);
|
||||
rates.max = max(rates.max, rate);
|
||||
}
|
||||
|
||||
return snd_interval_refine(r, &rates);
|
||||
}
|
||||
|
||||
static int dice_channels_constraint(struct snd_pcm_hw_params *params,
|
||||
struct snd_pcm_hw_rule *rule)
|
||||
{
|
||||
struct snd_pcm_substream *substream = rule->private;
|
||||
struct snd_dice *dice = substream->private_data;
|
||||
|
||||
const struct snd_interval *r =
|
||||
hw_param_interval_c(params, SNDRV_PCM_HW_PARAM_RATE);
|
||||
struct snd_interval *c =
|
||||
hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS);
|
||||
struct snd_interval channels = {
|
||||
.min = UINT_MAX, .max = 0, .integer = 1
|
||||
};
|
||||
unsigned int i, rate, mode, *pcm_channels;
|
||||
|
||||
if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
|
||||
pcm_channels = dice->tx_channels;
|
||||
else
|
||||
pcm_channels = dice->rx_channels;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(snd_dice_rates); ++i) {
|
||||
rate = snd_dice_rates[i];
|
||||
if (snd_dice_stream_get_rate_mode(dice, rate, &mode) < 0)
|
||||
continue;
|
||||
|
||||
if (!snd_interval_test(r, rate))
|
||||
continue;
|
||||
|
||||
channels.min = min(channels.min, pcm_channels[mode]);
|
||||
channels.max = max(channels.max, pcm_channels[mode]);
|
||||
}
|
||||
|
||||
return snd_interval_refine(c, &channels);
|
||||
}
|
||||
|
||||
static void limit_channels_and_rates(struct snd_dice *dice,
|
||||
struct snd_pcm_runtime *runtime,
|
||||
unsigned int *pcm_channels)
|
||||
static int limit_channels_and_rates(struct snd_dice *dice,
|
||||
struct snd_pcm_runtime *runtime,
|
||||
struct amdtp_stream *stream)
|
||||
{
|
||||
struct snd_pcm_hardware *hw = &runtime->hw;
|
||||
unsigned int i, rate, mode;
|
||||
unsigned int rate;
|
||||
__be32 reg[2];
|
||||
int err;
|
||||
|
||||
hw->channels_min = UINT_MAX;
|
||||
hw->channels_max = 0;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(snd_dice_rates); ++i) {
|
||||
rate = snd_dice_rates[i];
|
||||
if (snd_dice_stream_get_rate_mode(dice, rate, &mode) < 0)
|
||||
continue;
|
||||
hw->rates |= snd_pcm_rate_to_rate_bit(rate);
|
||||
|
||||
if (pcm_channels[mode] == 0)
|
||||
continue;
|
||||
hw->channels_min = min(hw->channels_min, pcm_channels[mode]);
|
||||
hw->channels_max = max(hw->channels_max, pcm_channels[mode]);
|
||||
/*
|
||||
* Retrieve current Multi Bit Linear Audio data channel and limit to
|
||||
* it.
|
||||
*/
|
||||
if (stream == &dice->tx_stream) {
|
||||
err = snd_dice_transaction_read_tx(dice, TX_NUMBER_AUDIO,
|
||||
reg, sizeof(reg));
|
||||
} else {
|
||||
err = snd_dice_transaction_read_rx(dice, RX_NUMBER_AUDIO,
|
||||
reg, sizeof(reg));
|
||||
}
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
hw->channels_min = hw->channels_max = be32_to_cpu(reg[0]);
|
||||
|
||||
/* Retrieve current sampling transfer frequency and limit to it. */
|
||||
err = snd_dice_transaction_get_rate(dice, &rate);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
hw->rates = snd_pcm_rate_to_rate_bit(rate);
|
||||
snd_pcm_limit_hw_rates(runtime);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void limit_period_and_buffer(struct snd_pcm_hardware *hw)
|
||||
@ -122,7 +63,6 @@ static int init_hw_info(struct snd_dice *dice,
|
||||
struct snd_pcm_runtime *runtime = substream->runtime;
|
||||
struct snd_pcm_hardware *hw = &runtime->hw;
|
||||
struct amdtp_stream *stream;
|
||||
unsigned int *pcm_channels;
|
||||
int err;
|
||||
|
||||
hw->info = SNDRV_PCM_INFO_MMAP |
|
||||
@ -135,37 +75,22 @@ static int init_hw_info(struct snd_dice *dice,
|
||||
if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) {
|
||||
hw->formats = AM824_IN_PCM_FORMAT_BITS;
|
||||
stream = &dice->tx_stream;
|
||||
pcm_channels = dice->tx_channels;
|
||||
} else {
|
||||
hw->formats = AM824_OUT_PCM_FORMAT_BITS;
|
||||
stream = &dice->rx_stream;
|
||||
pcm_channels = dice->rx_channels;
|
||||
}
|
||||
|
||||
limit_channels_and_rates(dice, runtime, pcm_channels);
|
||||
err = limit_channels_and_rates(dice, runtime, stream);
|
||||
if (err < 0)
|
||||
return err;
|
||||
limit_period_and_buffer(hw);
|
||||
|
||||
err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_RATE,
|
||||
dice_rate_constraint, substream,
|
||||
SNDRV_PCM_HW_PARAM_CHANNELS, -1);
|
||||
if (err < 0)
|
||||
goto end;
|
||||
err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS,
|
||||
dice_channels_constraint, substream,
|
||||
SNDRV_PCM_HW_PARAM_RATE, -1);
|
||||
if (err < 0)
|
||||
goto end;
|
||||
|
||||
err = amdtp_am824_add_pcm_hw_constraints(stream, runtime);
|
||||
end:
|
||||
return err;
|
||||
return amdtp_am824_add_pcm_hw_constraints(stream, runtime);
|
||||
}
|
||||
|
||||
static int pcm_open(struct snd_pcm_substream *substream)
|
||||
{
|
||||
struct snd_dice *dice = substream->private_data;
|
||||
unsigned int source, rate;
|
||||
bool internal;
|
||||
int err;
|
||||
|
||||
err = snd_dice_stream_lock_try(dice);
|
||||
@ -176,39 +101,6 @@ static int pcm_open(struct snd_pcm_substream *substream)
|
||||
if (err < 0)
|
||||
goto err_locked;
|
||||
|
||||
err = snd_dice_transaction_get_clock_source(dice, &source);
|
||||
if (err < 0)
|
||||
goto err_locked;
|
||||
switch (source) {
|
||||
case CLOCK_SOURCE_AES1:
|
||||
case CLOCK_SOURCE_AES2:
|
||||
case CLOCK_SOURCE_AES3:
|
||||
case CLOCK_SOURCE_AES4:
|
||||
case CLOCK_SOURCE_AES_ANY:
|
||||
case CLOCK_SOURCE_ADAT:
|
||||
case CLOCK_SOURCE_TDIF:
|
||||
case CLOCK_SOURCE_WC:
|
||||
internal = false;
|
||||
break;
|
||||
default:
|
||||
internal = true;
|
||||
break;
|
||||
}
|
||||
|
||||
/*
|
||||
* When source of clock is not internal or any PCM streams are running,
|
||||
* available sampling rate is limited at current sampling rate.
|
||||
*/
|
||||
if (!internal ||
|
||||
amdtp_stream_pcm_running(&dice->tx_stream) ||
|
||||
amdtp_stream_pcm_running(&dice->rx_stream)) {
|
||||
err = snd_dice_transaction_get_rate(dice, &rate);
|
||||
if (err < 0)
|
||||
goto err_locked;
|
||||
substream->runtime->hw.rate_min = rate;
|
||||
substream->runtime->hw.rate_max = rate;
|
||||
}
|
||||
|
||||
snd_pcm_set_sync(substream);
|
||||
end:
|
||||
return err;
|
||||
|
Loading…
Reference in New Issue
Block a user