ALSA: dice: use cache for PCM constraints and rules
In former commits, proxy structure gets members for cache of stream formats. The cache allows to apply correct constraints and rules to runtime of PCM substream. They allows userspace applications to change current sampling transmission frequency. This commit uses the cacher for the PCM constraints and rules. Signed-off-by: Takashi Sakamoto <o-takashi@sakamocchi.jp> Signed-off-by: Takashi Iwai <tiwai@suse.de>
This commit is contained in:
parent
b8f78234aa
commit
bd2b441c57
|
@ -9,43 +9,115 @@
|
|||
|
||||
#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;
|
||||
unsigned int index = substream->pcm->device;
|
||||
|
||||
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 *pcm_channels;
|
||||
enum snd_dice_rate_mode mode;
|
||||
unsigned int i, rate;
|
||||
|
||||
if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
|
||||
pcm_channels = dice->tx_pcm_chs[index];
|
||||
else
|
||||
pcm_channels = dice->rx_pcm_chs[index];
|
||||
|
||||
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;
|
||||
unsigned int index = substream->pcm->device;
|
||||
|
||||
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 *pcm_channels;
|
||||
enum snd_dice_rate_mode mode;
|
||||
unsigned int i, rate;
|
||||
|
||||
if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
|
||||
pcm_channels = dice->tx_pcm_chs[index];
|
||||
else
|
||||
pcm_channels = dice->rx_pcm_chs[index];
|
||||
|
||||
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 int limit_channels_and_rates(struct snd_dice *dice,
|
||||
struct snd_pcm_runtime *runtime,
|
||||
enum amdtp_stream_direction dir,
|
||||
unsigned int index, unsigned int size)
|
||||
unsigned int index)
|
||||
{
|
||||
struct snd_pcm_hardware *hw = &runtime->hw;
|
||||
struct amdtp_stream *stream;
|
||||
unsigned int rate;
|
||||
__be32 reg;
|
||||
int err;
|
||||
unsigned int *pcm_channels;
|
||||
unsigned int i;
|
||||
|
||||
/*
|
||||
* Retrieve current Multi Bit Linear Audio data channel and limit to
|
||||
* it.
|
||||
*/
|
||||
if (dir == AMDTP_IN_STREAM) {
|
||||
stream = &dice->tx_stream[index];
|
||||
err = snd_dice_transaction_read_tx(dice,
|
||||
size * index + TX_NUMBER_AUDIO,
|
||||
®, sizeof(reg));
|
||||
} else {
|
||||
stream = &dice->rx_stream[index];
|
||||
err = snd_dice_transaction_read_rx(dice,
|
||||
size * index + RX_NUMBER_AUDIO,
|
||||
®, sizeof(reg));
|
||||
if (dir == AMDTP_IN_STREAM)
|
||||
pcm_channels = dice->tx_pcm_chs[index];
|
||||
else
|
||||
pcm_channels = dice->rx_pcm_chs[index];
|
||||
|
||||
hw->channels_min = UINT_MAX;
|
||||
hw->channels_max = 0;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(snd_dice_rates); ++i) {
|
||||
enum snd_dice_rate_mode mode;
|
||||
unsigned int rate, channels;
|
||||
|
||||
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);
|
||||
|
||||
channels = pcm_channels[mode];
|
||||
if (channels == 0)
|
||||
continue;
|
||||
hw->channels_min = min(hw->channels_min, channels);
|
||||
hw->channels_max = max(hw->channels_max, channels);
|
||||
}
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
hw->channels_min = hw->channels_max = be32_to_cpu(reg);
|
||||
|
||||
/* 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;
|
||||
|
@ -56,36 +128,34 @@ static int init_hw_info(struct snd_dice *dice,
|
|||
{
|
||||
struct snd_pcm_runtime *runtime = substream->runtime;
|
||||
struct snd_pcm_hardware *hw = &runtime->hw;
|
||||
unsigned int index = substream->pcm->device;
|
||||
enum amdtp_stream_direction dir;
|
||||
struct amdtp_stream *stream;
|
||||
__be32 reg[2];
|
||||
unsigned int count, size;
|
||||
int err;
|
||||
|
||||
if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) {
|
||||
hw->formats = AM824_IN_PCM_FORMAT_BITS;
|
||||
dir = AMDTP_IN_STREAM;
|
||||
stream = &dice->tx_stream[substream->pcm->device];
|
||||
err = snd_dice_transaction_read_tx(dice, TX_NUMBER, reg,
|
||||
sizeof(reg));
|
||||
stream = &dice->tx_stream[index];
|
||||
} else {
|
||||
hw->formats = AM824_OUT_PCM_FORMAT_BITS;
|
||||
dir = AMDTP_OUT_STREAM;
|
||||
stream = &dice->rx_stream[substream->pcm->device];
|
||||
err = snd_dice_transaction_read_rx(dice, RX_NUMBER, reg,
|
||||
sizeof(reg));
|
||||
stream = &dice->rx_stream[index];
|
||||
}
|
||||
|
||||
err = limit_channels_and_rates(dice, substream->runtime, dir,
|
||||
index);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
count = min_t(unsigned int, be32_to_cpu(reg[0]), MAX_STREAMS);
|
||||
if (substream->pcm->device >= count)
|
||||
return -ENXIO;
|
||||
|
||||
size = be32_to_cpu(reg[1]) * 4;
|
||||
err = limit_channels_and_rates(dice, substream->runtime, dir,
|
||||
substream->pcm->device, size);
|
||||
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)
|
||||
return err;
|
||||
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)
|
||||
return err;
|
||||
|
||||
|
@ -95,6 +165,8 @@ static int init_hw_info(struct snd_dice *dice,
|
|||
static int pcm_open(struct snd_pcm_substream *substream)
|
||||
{
|
||||
struct snd_dice *dice = substream->private_data;
|
||||
unsigned int source;
|
||||
bool internal;
|
||||
int err;
|
||||
|
||||
err = snd_dice_stream_lock_try(dice);
|
||||
|
@ -105,6 +177,43 @@ 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[0]) ||
|
||||
amdtp_stream_pcm_running(&dice->tx_stream[1]) ||
|
||||
amdtp_stream_pcm_running(&dice->rx_stream[0]) ||
|
||||
amdtp_stream_pcm_running(&dice->rx_stream[1])) {
|
||||
unsigned int rate;
|
||||
|
||||
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;
|
||||
|
@ -318,7 +427,6 @@ int snd_dice_create_pcm(struct snd_dice *dice)
|
|||
.page = snd_pcm_lib_get_vmalloc_page,
|
||||
.mmap = snd_pcm_lib_mmap_vmalloc,
|
||||
};
|
||||
__be32 reg;
|
||||
struct snd_pcm *pcm;
|
||||
unsigned int i, max_capture, max_playback, capture, playback;
|
||||
int err;
|
||||
|
@ -327,18 +435,23 @@ int snd_dice_create_pcm(struct snd_dice *dice)
|
|||
if (dice->force_two_pcms) {
|
||||
max_capture = max_playback = 2;
|
||||
} else {
|
||||
int j;
|
||||
max_capture = max_playback = 0;
|
||||
err = snd_dice_transaction_read_tx(dice, TX_NUMBER, ®,
|
||||
sizeof(reg));
|
||||
if (err < 0)
|
||||
return err;
|
||||
max_capture = min_t(unsigned int, be32_to_cpu(reg), MAX_STREAMS);
|
||||
for (i = 0; i < MAX_STREAMS; ++i) {
|
||||
for (j = 0; j < SND_DICE_RATE_MODE_COUNT; ++j) {
|
||||
if (dice->tx_pcm_chs[i][j] > 0) {
|
||||
++max_capture;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
err = snd_dice_transaction_read_rx(dice, RX_NUMBER, ®,
|
||||
sizeof(reg));
|
||||
if (err < 0)
|
||||
return err;
|
||||
max_playback = min_t(unsigned int, be32_to_cpu(reg), MAX_STREAMS);
|
||||
for (j = 0; j < SND_DICE_RATE_MODE_COUNT; ++j) {
|
||||
if (dice->rx_pcm_chs[i][j] > 0) {
|
||||
++max_playback;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (i = 0; i < MAX_STREAMS; i++) {
|
||||
|
|
Loading…
Reference in New Issue