This commit adds a support for capturing PCM samples.
When opposite PCM substream is already running, available sampling rate is limited at current one.
Signed-off-by: Takashi Sakamoto o-takashi@sakamocchi.jp --- sound/firewire/Kconfig | 3 - sound/firewire/dice/dice-pcm.c | 147 +++++++++++++++++++++++++++++++++++++---- 2 files changed, 133 insertions(+), 17 deletions(-)
diff --git a/sound/firewire/Kconfig b/sound/firewire/Kconfig index 46dff64..c8e704c 100644 --- a/sound/firewire/Kconfig +++ b/sound/firewire/Kconfig @@ -20,9 +20,6 @@ config SND_DICE Say Y here to include support for many DACs based on the DICE chip family (DICE-II/Jr/Mini) from TC Applied Technologies.
- At the moment, this driver supports playback only. If you - want to use devices that support capturing, use FFADO instead. - To compile this driver as a module, choose M here: the module will be called snd-dice.
diff --git a/sound/firewire/dice/dice-pcm.c b/sound/firewire/dice/dice-pcm.c index c7d84bc..3427a21 100644 --- a/sound/firewire/dice/dice-pcm.c +++ b/sound/firewire/dice/dice-pcm.c @@ -12,7 +12,8 @@ static int dice_rate_constraint(struct snd_pcm_hw_params *params, struct snd_pcm_hw_rule *rule) { - struct snd_dice *dice = rule->private; + 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); @@ -21,7 +22,12 @@ static int dice_rate_constraint(struct snd_pcm_hw_params *params, struct snd_interval rates = { .min = UINT_MAX, .max = 0, .integer = 1 }; - unsigned int i, rate, mode, *pcm_channels = dice->rx_channels; + 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]; @@ -41,7 +47,8 @@ static int dice_rate_constraint(struct snd_pcm_hw_params *params, static int dice_channels_constraint(struct snd_pcm_hw_params *params, struct snd_pcm_hw_rule *rule) { - struct snd_dice *dice = rule->private; + 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); @@ -50,7 +57,12 @@ static int dice_channels_constraint(struct snd_pcm_hw_params *params, struct snd_interval channels = { .min = UINT_MAX, .max = 0, .integer = 1 }; - unsigned int i, rate, mode, *pcm_channels = dice->rx_channels; + 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]; @@ -109,30 +121,42 @@ 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 | SNDRV_PCM_INFO_MMAP_VALID | SNDRV_PCM_INFO_BATCH | SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_JOINT_DUPLEX | SNDRV_PCM_INFO_BLOCK_TRANSFER; - hw->formats = AMDTP_OUT_PCM_FORMAT_BITS;
- limit_channels_and_rates(dice, runtime, dice->rx_channels); + if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) { + hw->formats = AMDTP_IN_PCM_FORMAT_BITS; + stream = &dice->tx_stream; + pcm_channels = dice->tx_channels; + } else { + hw->formats = AMDTP_OUT_PCM_FORMAT_BITS; + stream = &dice->rx_stream; + pcm_channels = dice->rx_channels; + } + + 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, dice, + 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, dice, + dice_channels_constraint, substream, SNDRV_PCM_HW_PARAM_RATE, -1); if (err < 0) goto end;
- err = amdtp_stream_add_pcm_hw_constraints(&dice->rx_stream, runtime); + err = amdtp_stream_add_pcm_hw_constraints(stream, runtime); end: return err; } @@ -172,10 +196,12 @@ static int pcm_open(struct snd_pcm_substream *substream) }
/* - * When source of clock is not internal, available sampling rate is - * limited at current sampling rate. + * When source of clock is not internal or any PCM streams are running, + * available sampling rate is limited at current sampling rate. */ - if (!internal) { + 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; @@ -200,10 +226,28 @@ static int pcm_close(struct snd_pcm_substream *substream) return 0; }
+static int capture_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *hw_params) +{ + struct snd_dice *dice = substream->private_data; + + if (substream->runtime->status->state == SNDRV_PCM_STATE_OPEN) + atomic_inc(&dice->substreams_counter); + + amdtp_stream_set_pcm_format(&dice->tx_stream, + params_format(hw_params)); + + return snd_pcm_lib_alloc_vmalloc_buffer(substream, + params_buffer_bytes(hw_params)); +} static int playback_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *hw_params) { struct snd_dice *dice = substream->private_data; + + if (substream->runtime->status->state == SNDRV_PCM_STATE_OPEN) + atomic_inc(&dice->substreams_counter); + amdtp_stream_set_pcm_format(&dice->rx_stream, params_format(hw_params));
@@ -211,15 +255,41 @@ static int playback_hw_params(struct snd_pcm_substream *substream, params_buffer_bytes(hw_params)); }
+static int capture_hw_free(struct snd_pcm_substream *substream) +{ + struct snd_dice *dice = substream->private_data; + + if (substream->runtime->status->state != SNDRV_PCM_STATE_OPEN) + atomic_dec(&dice->substreams_counter); + + snd_dice_stream_stop_duplex(dice); + + return snd_pcm_lib_free_vmalloc_buffer(substream); +} + static int playback_hw_free(struct snd_pcm_substream *substream) { struct snd_dice *dice = substream->private_data;
+ if (substream->runtime->status->state != SNDRV_PCM_STATE_OPEN) + atomic_dec(&dice->substreams_counter); + snd_dice_stream_stop_duplex(dice);
return snd_pcm_lib_free_vmalloc_buffer(substream); }
+static int capture_prepare(struct snd_pcm_substream *substream) +{ + struct snd_dice *dice = substream->private_data; + int err; + + err = snd_dice_stream_start_duplex(dice, substream->runtime->rate); + if (err >= 0) + amdtp_stream_pcm_prepare(&dice->tx_stream); + + return 0; +} static int playback_prepare(struct snd_pcm_substream *substream) { struct snd_dice *dice = substream->private_data; @@ -232,6 +302,23 @@ static int playback_prepare(struct snd_pcm_substream *substream) return err; }
+static int capture_trigger(struct snd_pcm_substream *substream, int cmd) +{ + struct snd_dice *dice = substream->private_data; + + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + amdtp_stream_pcm_trigger(&dice->tx_stream, substream); + break; + case SNDRV_PCM_TRIGGER_STOP: + amdtp_stream_pcm_trigger(&dice->tx_stream, NULL); + break; + default: + return -EINVAL; + } + + return 0; +} static int playback_trigger(struct snd_pcm_substream *substream, int cmd) { struct snd_dice *dice = substream->private_data; @@ -250,6 +337,12 @@ static int playback_trigger(struct snd_pcm_substream *substream, int cmd) return 0; }
+static snd_pcm_uframes_t capture_pointer(struct snd_pcm_substream *substream) +{ + struct snd_dice *dice = substream->private_data; + + return amdtp_stream_pcm_pointer(&dice->tx_stream); +} static snd_pcm_uframes_t playback_pointer(struct snd_pcm_substream *substream) { struct snd_dice *dice = substream->private_data; @@ -259,6 +352,18 @@ static snd_pcm_uframes_t playback_pointer(struct snd_pcm_substream *substream)
int snd_dice_create_pcm(struct snd_dice *dice) { + static struct snd_pcm_ops capture_ops = { + .open = pcm_open, + .close = pcm_close, + .ioctl = snd_pcm_lib_ioctl, + .hw_params = capture_hw_params, + .hw_free = capture_hw_free, + .prepare = capture_prepare, + .trigger = capture_trigger, + .pointer = capture_pointer, + .page = snd_pcm_lib_get_vmalloc_page, + .mmap = snd_pcm_lib_mmap_vmalloc, + }; static struct snd_pcm_ops playback_ops = { .open = pcm_open, .close = pcm_close, @@ -272,14 +377,28 @@ int snd_dice_create_pcm(struct snd_dice *dice) .mmap = snd_pcm_lib_mmap_vmalloc, }; struct snd_pcm *pcm; + unsigned int i, capture, playback; int err;
- err = snd_pcm_new(dice->card, "DICE", 0, 1, 0, &pcm); + capture = playback = 0; + for (i = 0; i < 3; i++) { + if (dice->tx_channels[i] > 0) + capture = 1; + if (dice->rx_channels[i] > 0) + playback = 1; + } + + 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); - snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &playback_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);
return 0; }