Commit bd2b441c authored by Takashi Sakamoto's avatar Takashi Sakamoto Committed by Takashi Iwai

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: default avatarTakashi Sakamoto <o-takashi@sakamocchi.jp>
Signed-off-by: default avatarTakashi Iwai <tiwai@suse.de>
parent b8f78234
...@@ -9,43 +9,115 @@ ...@@ -9,43 +9,115 @@
#include "dice.h" #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, static int limit_channels_and_rates(struct snd_dice *dice,
struct snd_pcm_runtime *runtime, struct snd_pcm_runtime *runtime,
enum amdtp_stream_direction dir, enum amdtp_stream_direction dir,
unsigned int index, unsigned int size) unsigned int index)
{ {
struct snd_pcm_hardware *hw = &runtime->hw; struct snd_pcm_hardware *hw = &runtime->hw;
struct amdtp_stream *stream; unsigned int *pcm_channels;
unsigned int rate; unsigned int i;
__be32 reg;
int err; if (dir == AMDTP_IN_STREAM)
pcm_channels = dice->tx_pcm_chs[index];
/* else
* Retrieve current Multi Bit Linear Audio data channel and limit to pcm_channels = dice->rx_pcm_chs[index];
* it.
*/ hw->channels_min = UINT_MAX;
if (dir == AMDTP_IN_STREAM) { hw->channels_max = 0;
stream = &dice->tx_stream[index];
err = snd_dice_transaction_read_tx(dice, for (i = 0; i < ARRAY_SIZE(snd_dice_rates); ++i) {
size * index + TX_NUMBER_AUDIO, enum snd_dice_rate_mode mode;
&reg, sizeof(reg)); unsigned int rate, channels;
} else {
stream = &dice->rx_stream[index]; rate = snd_dice_rates[i];
err = snd_dice_transaction_read_rx(dice, if (snd_dice_stream_get_rate_mode(dice, rate, &mode) < 0)
size * index + RX_NUMBER_AUDIO, continue;
&reg, sizeof(reg)); 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); snd_pcm_limit_hw_rates(runtime);
return 0; return 0;
...@@ -56,36 +128,34 @@ static int init_hw_info(struct snd_dice *dice, ...@@ -56,36 +128,34 @@ static int init_hw_info(struct snd_dice *dice,
{ {
struct snd_pcm_runtime *runtime = substream->runtime; struct snd_pcm_runtime *runtime = substream->runtime;
struct snd_pcm_hardware *hw = &runtime->hw; struct snd_pcm_hardware *hw = &runtime->hw;
unsigned int index = substream->pcm->device;
enum amdtp_stream_direction dir; enum amdtp_stream_direction dir;
struct amdtp_stream *stream; struct amdtp_stream *stream;
__be32 reg[2];
unsigned int count, size;
int err; int err;
if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) { if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) {
hw->formats = AM824_IN_PCM_FORMAT_BITS; hw->formats = AM824_IN_PCM_FORMAT_BITS;
dir = AMDTP_IN_STREAM; dir = AMDTP_IN_STREAM;
stream = &dice->tx_stream[substream->pcm->device]; stream = &dice->tx_stream[index];
err = snd_dice_transaction_read_tx(dice, TX_NUMBER, reg,
sizeof(reg));
} else { } else {
hw->formats = AM824_OUT_PCM_FORMAT_BITS; hw->formats = AM824_OUT_PCM_FORMAT_BITS;
dir = AMDTP_OUT_STREAM; dir = AMDTP_OUT_STREAM;
stream = &dice->rx_stream[substream->pcm->device]; stream = &dice->rx_stream[index];
err = snd_dice_transaction_read_rx(dice, RX_NUMBER, reg,
sizeof(reg));
} }
err = limit_channels_and_rates(dice, substream->runtime, dir,
index);
if (err < 0) if (err < 0)
return err; return err;
count = min_t(unsigned int, be32_to_cpu(reg[0]), MAX_STREAMS); err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_RATE,
if (substream->pcm->device >= count) dice_rate_constraint, substream,
return -ENXIO; SNDRV_PCM_HW_PARAM_CHANNELS, -1);
if (err < 0)
size = be32_to_cpu(reg[1]) * 4; return err;
err = limit_channels_and_rates(dice, substream->runtime, dir, err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS,
substream->pcm->device, size); dice_channels_constraint, substream,
SNDRV_PCM_HW_PARAM_RATE, -1);
if (err < 0) if (err < 0)
return err; return err;
...@@ -95,6 +165,8 @@ static int init_hw_info(struct snd_dice *dice, ...@@ -95,6 +165,8 @@ static int init_hw_info(struct snd_dice *dice,
static int pcm_open(struct snd_pcm_substream *substream) static int pcm_open(struct snd_pcm_substream *substream)
{ {
struct snd_dice *dice = substream->private_data; struct snd_dice *dice = substream->private_data;
unsigned int source;
bool internal;
int err; int err;
err = snd_dice_stream_lock_try(dice); err = snd_dice_stream_lock_try(dice);
...@@ -105,6 +177,43 @@ static int pcm_open(struct snd_pcm_substream *substream) ...@@ -105,6 +177,43 @@ static int pcm_open(struct snd_pcm_substream *substream)
if (err < 0) if (err < 0)
goto err_locked; 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); snd_pcm_set_sync(substream);
end: end:
return err; return err;
...@@ -318,7 +427,6 @@ int snd_dice_create_pcm(struct snd_dice *dice) ...@@ -318,7 +427,6 @@ int snd_dice_create_pcm(struct snd_dice *dice)
.page = snd_pcm_lib_get_vmalloc_page, .page = snd_pcm_lib_get_vmalloc_page,
.mmap = snd_pcm_lib_mmap_vmalloc, .mmap = snd_pcm_lib_mmap_vmalloc,
}; };
__be32 reg;
struct snd_pcm *pcm; struct snd_pcm *pcm;
unsigned int i, max_capture, max_playback, capture, playback; unsigned int i, max_capture, max_playback, capture, playback;
int err; int err;
...@@ -327,18 +435,23 @@ int snd_dice_create_pcm(struct snd_dice *dice) ...@@ -327,18 +435,23 @@ int snd_dice_create_pcm(struct snd_dice *dice)
if (dice->force_two_pcms) { if (dice->force_two_pcms) {
max_capture = max_playback = 2; max_capture = max_playback = 2;
} else { } else {
int j;
max_capture = max_playback = 0; max_capture = max_playback = 0;
err = snd_dice_transaction_read_tx(dice, TX_NUMBER, &reg, for (i = 0; i < MAX_STREAMS; ++i) {
sizeof(reg)); for (j = 0; j < SND_DICE_RATE_MODE_COUNT; ++j) {
if (err < 0) if (dice->tx_pcm_chs[i][j] > 0) {
return err; ++max_capture;
max_capture = min_t(unsigned int, be32_to_cpu(reg), MAX_STREAMS); break;
}
err = snd_dice_transaction_read_rx(dice, RX_NUMBER, &reg, }
sizeof(reg));
if (err < 0) for (j = 0; j < SND_DICE_RATE_MODE_COUNT; ++j) {
return err; if (dice->rx_pcm_chs[i][j] > 0) {
max_playback = min_t(unsigned int, be32_to_cpu(reg), MAX_STREAMS); ++max_playback;
break;
}
}
}
} }
for (i = 0; i < MAX_STREAMS; i++) { for (i = 0; i < MAX_STREAMS; i++) {
......
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