summaryrefslogtreecommitdiff
path: root/sound/firewire/dice/dice-pcm.c
diff options
context:
space:
mode:
authorAndré Fabian Silva Delgado <emulatorman@parabola.nu>2016-06-10 05:30:17 -0300
committerAndré Fabian Silva Delgado <emulatorman@parabola.nu>2016-06-10 05:30:17 -0300
commitd635711daa98be86d4c7fd01499c34f566b54ccb (patch)
treeaa5cc3760a27c3d57146498cb82fa549547de06c /sound/firewire/dice/dice-pcm.c
parentc91265cd0efb83778f015b4d4b1129bd2cfd075e (diff)
Linux-libre 4.6.2-gnu
Diffstat (limited to 'sound/firewire/dice/dice-pcm.c')
-rw-r--r--sound/firewire/dice/dice-pcm.c278
1 files changed, 113 insertions, 165 deletions
diff --git a/sound/firewire/dice/dice-pcm.c b/sound/firewire/dice/dice-pcm.c
index 9b3431999..4aa024982 100644
--- a/sound/firewire/dice/dice-pcm.c
+++ b/sound/firewire/dice/dice-pcm.c
@@ -9,99 +9,46 @@
#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,
+ enum amdtp_stream_direction dir,
+ unsigned int index, unsigned int size)
{
struct snd_pcm_hardware *hw = &runtime->hw;
- unsigned int i, rate, mode;
+ struct amdtp_stream *stream;
+ unsigned int rate;
+ __be32 reg;
+ int err;
- hw->channels_min = UINT_MAX;
- hw->channels_max = 0;
+ /*
+ * 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,
+ &reg, sizeof(reg));
+ } else {
+ stream = &dice->rx_stream[index];
+ err = snd_dice_transaction_read_rx(dice,
+ size * index + RX_NUMBER_AUDIO,
+ &reg, sizeof(reg));
+ }
+ if (err < 0)
+ return err;
- 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);
+ hw->channels_min = hw->channels_max = be32_to_cpu(reg);
- 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 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)
@@ -121,8 +68,10 @@ static int init_hw_info(struct snd_dice *dice,
{
struct snd_pcm_runtime *runtime = substream->runtime;
struct snd_pcm_hardware *hw = &runtime->hw;
+ enum amdtp_stream_direction dir;
struct amdtp_stream *stream;
- unsigned int *pcm_channels;
+ __be32 reg[2];
+ unsigned int count, size;
int err;
hw->info = SNDRV_PCM_INFO_MMAP |
@@ -134,38 +83,38 @@ 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;
+ dir = AMDTP_IN_STREAM;
+ stream = &dice->tx_stream[substream->pcm->device];
+ err = snd_dice_transaction_read_tx(dice, TX_NUMBER, reg,
+ sizeof(reg));
} else {
hw->formats = AM824_OUT_PCM_FORMAT_BITS;
- stream = &dice->rx_stream;
- pcm_channels = dice->rx_channels;
+ dir = AMDTP_OUT_STREAM;
+ stream = &dice->rx_stream[substream->pcm->device];
+ err = snd_dice_transaction_read_rx(dice, RX_NUMBER, reg,
+ sizeof(reg));
}
- limit_channels_and_rates(dice, runtime, pcm_channels);
- 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);
+ 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);
if (err < 0)
- goto end;
+ return err;
+ limit_period_and_buffer(hw);
- 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 +125,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;
@@ -230,6 +146,7 @@ static int capture_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *hw_params)
{
struct snd_dice *dice = substream->private_data;
+ struct amdtp_stream *stream = &dice->tx_stream[substream->pcm->device];
int err;
err = snd_pcm_lib_alloc_vmalloc_buffer(substream,
@@ -243,7 +160,7 @@ static int capture_hw_params(struct snd_pcm_substream *substream,
mutex_unlock(&dice->mutex);
}
- amdtp_am824_set_pcm_format(&dice->tx_stream, params_format(hw_params));
+ amdtp_am824_set_pcm_format(stream, params_format(hw_params));
return 0;
}
@@ -251,6 +168,7 @@ static int playback_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *hw_params)
{
struct snd_dice *dice = substream->private_data;
+ struct amdtp_stream *stream = &dice->rx_stream[substream->pcm->device];
int err;
err = snd_pcm_lib_alloc_vmalloc_buffer(substream,
@@ -264,7 +182,7 @@ static int playback_hw_params(struct snd_pcm_substream *substream,
mutex_unlock(&dice->mutex);
}
- amdtp_am824_set_pcm_format(&dice->rx_stream, params_format(hw_params));
+ amdtp_am824_set_pcm_format(stream, params_format(hw_params));
return 0;
}
@@ -304,26 +222,28 @@ static int playback_hw_free(struct snd_pcm_substream *substream)
static int capture_prepare(struct snd_pcm_substream *substream)
{
struct snd_dice *dice = substream->private_data;
+ struct amdtp_stream *stream = &dice->tx_stream[substream->pcm->device];
int err;
mutex_lock(&dice->mutex);
err = snd_dice_stream_start_duplex(dice, substream->runtime->rate);
mutex_unlock(&dice->mutex);
if (err >= 0)
- amdtp_stream_pcm_prepare(&dice->tx_stream);
+ amdtp_stream_pcm_prepare(stream);
return 0;
}
static int playback_prepare(struct snd_pcm_substream *substream)
{
struct snd_dice *dice = substream->private_data;
+ struct amdtp_stream *stream = &dice->rx_stream[substream->pcm->device];
int err;
mutex_lock(&dice->mutex);
err = snd_dice_stream_start_duplex(dice, substream->runtime->rate);
mutex_unlock(&dice->mutex);
if (err >= 0)
- amdtp_stream_pcm_prepare(&dice->rx_stream);
+ amdtp_stream_pcm_prepare(stream);
return err;
}
@@ -331,13 +251,14 @@ static int playback_prepare(struct snd_pcm_substream *substream)
static int capture_trigger(struct snd_pcm_substream *substream, int cmd)
{
struct snd_dice *dice = substream->private_data;
+ struct amdtp_stream *stream = &dice->tx_stream[substream->pcm->device];
switch (cmd) {
case SNDRV_PCM_TRIGGER_START:
- amdtp_stream_pcm_trigger(&dice->tx_stream, substream);
+ amdtp_stream_pcm_trigger(stream, substream);
break;
case SNDRV_PCM_TRIGGER_STOP:
- amdtp_stream_pcm_trigger(&dice->tx_stream, NULL);
+ amdtp_stream_pcm_trigger(stream, NULL);
break;
default:
return -EINVAL;
@@ -348,13 +269,14 @@ static int capture_trigger(struct snd_pcm_substream *substream, int cmd)
static int playback_trigger(struct snd_pcm_substream *substream, int cmd)
{
struct snd_dice *dice = substream->private_data;
+ struct amdtp_stream *stream = &dice->rx_stream[substream->pcm->device];
switch (cmd) {
case SNDRV_PCM_TRIGGER_START:
- amdtp_stream_pcm_trigger(&dice->rx_stream, substream);
+ amdtp_stream_pcm_trigger(stream, substream);
break;
case SNDRV_PCM_TRIGGER_STOP:
- amdtp_stream_pcm_trigger(&dice->rx_stream, NULL);
+ amdtp_stream_pcm_trigger(stream, NULL);
break;
default:
return -EINVAL;
@@ -366,14 +288,16 @@ static int playback_trigger(struct snd_pcm_substream *substream, int cmd)
static snd_pcm_uframes_t capture_pointer(struct snd_pcm_substream *substream)
{
struct snd_dice *dice = substream->private_data;
+ struct amdtp_stream *stream = &dice->tx_stream[substream->pcm->device];
- return amdtp_stream_pcm_pointer(&dice->tx_stream);
+ return amdtp_stream_pcm_pointer(stream);
}
static snd_pcm_uframes_t playback_pointer(struct snd_pcm_substream *substream)
{
struct snd_dice *dice = substream->private_data;
+ struct amdtp_stream *stream = &dice->rx_stream[substream->pcm->device];
- return amdtp_stream_pcm_pointer(&dice->rx_stream);
+ return amdtp_stream_pcm_pointer(stream);
}
int snd_dice_create_pcm(struct snd_dice *dice)
@@ -402,29 +326,53 @@ 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, capture, playback;
+ unsigned int i, max_capture, max_playback, capture, playback;
int err;
- capture = playback = 0;
- for (i = 0; i < 3; i++) {
- if (dice->tx_channels[i] > 0)
+ /* Check whether PCM substreams are required. */
+ if (dice->force_two_pcms) {
+ max_capture = max_playback = 2;
+ } else {
+ max_capture = max_playback = 0;
+ err = snd_dice_transaction_read_tx(dice, TX_NUMBER, &reg,
+ sizeof(reg));
+ if (err < 0)
+ return err;
+ max_capture = min_t(unsigned int, be32_to_cpu(reg), MAX_STREAMS);
+
+ err = snd_dice_transaction_read_rx(dice, RX_NUMBER, &reg,
+ sizeof(reg));
+ if (err < 0)
+ return err;
+ max_playback = min_t(unsigned int, be32_to_cpu(reg), MAX_STREAMS);
+ }
+
+ for (i = 0; i < MAX_STREAMS; i++) {
+ capture = playback = 0;
+ if (i < max_capture)
capture = 1;
- if (dice->rx_channels[i] > 0)
+ if (i < max_playback)
playback = 1;
- }
+ if (capture == 0 && playback == 0)
+ break;
- err = snd_pcm_new(dice->card, "DICE", 0, playback, capture, &pcm);
- if (err < 0)
- return err;
- pcm->private_data = dice;
- strcpy(pcm->name, dice->card->shortname);
+ err = snd_pcm_new(dice->card, "DICE", i, playback, capture,
+ &pcm);
+ if (err < 0)
+ return err;
+ pcm->private_data = dice;
+ strcpy(pcm->name, dice->card->shortname);
- if (capture > 0)
- snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &capture_ops);
+ if (capture > 0)
+ snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE,
+ &capture_ops);
- if (playback > 0)
- snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &playback_ops);
+ if (playback > 0)
+ snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK,
+ &playback_ops);
+ }
return 0;
}