struct snd_pcm_hw_params *hw_params)
{
struct snd_dice *dice = substream->private_data;
- unsigned int mode, rate, channels, i;
- int err;
-
- mutex_lock(&dice->mutex);
- snd_dice_stream_stop(dice);
- mutex_unlock(&dice->mutex);
-
- err = snd_pcm_lib_alloc_vmalloc_buffer(substream,
- params_buffer_bytes(hw_params));
- if (err < 0)
- return err;
-
- rate = params_rate(hw_params);
- err = snd_dice_transaction_set_rate(dice, rate);
- if (err < 0)
- return err;
-
- if (snd_dice_stream_get_rate_mode(dice, rate, &mode) < 0)
- return err;
-
- /*
- * At 176.4/192.0 kHz, Dice has a quirk to transfer two PCM frames in
- * one data block of AMDTP packet. Thus sampling transfer frequency is
- * a half of PCM sampling frequency, i.e. PCM frames at 192.0 kHz are
- * transferred on AMDTP packets at 96 kHz. Two successive samples of a
- * channel are stored consecutively in the packet. This quirk is called
- * as 'Dual Wire'.
- * For this quirk, blocking mode is required and PCM buffer size should
- * be aligned to SYT_INTERVAL.
- */
- channels = params_channels(hw_params);
- if (mode > 1) {
- if (channels > AMDTP_MAX_CHANNELS_FOR_PCM / 2) {
- err = -ENOSYS;
- return err;
- }
-
- rate /= 2;
- channels *= 2;
- dice->rx_stream.double_pcm_frames = true;
- } else {
- dice->rx_stream.double_pcm_frames = false;
- }
-
- amdtp_stream_set_parameters(&dice->rx_stream, rate, channels,
- dice->rx_midi_ports[mode]);
- if (mode > 1) {
- channels /= 2;
-
- for (i = 0; i < channels; i++) {
- dice->rx_stream.pcm_positions[i] = i * 2;
- dice->rx_stream.pcm_positions[i + channels] = i * 2 + 1;
- }
- }
-
amdtp_stream_set_pcm_format(&dice->rx_stream,
params_format(hw_params));
- return 0;
+ return snd_pcm_lib_alloc_vmalloc_buffer(substream,
+ params_buffer_bytes(hw_params));
}
static int playback_hw_free(struct snd_pcm_substream *substream)
int err;
mutex_lock(&dice->mutex);
-
- if (amdtp_streaming_error(&dice->rx_stream))
- snd_dice_stream_stop_packets(dice);
-
- err = snd_dice_stream_start(dice);
- if (err < 0) {
- mutex_unlock(&dice->mutex);
- return err;
- }
-
+ err = snd_dice_stream_start(dice, substream->runtime->rate);
mutex_unlock(&dice->mutex);
+ if (err >= 0)
+ amdtp_stream_pcm_prepare(&dice->rx_stream);
- amdtp_stream_pcm_prepare(&dice->rx_stream);
-
- return 0;
+ return err;
}
static int playback_trigger(struct snd_pcm_substream *substream, int cmd)
#include "dice.h"
+#define CALLBACK_TIMEOUT 200
+
const unsigned int snd_dice_rates[SND_DICE_RATES_COUNT] = {
/* mode 0 */
[0] = 32000,
return -EINVAL;
}
-int snd_dice_stream_start_packets(struct snd_dice *dice)
+static void release_resources(struct snd_dice *dice)
{
- int err;
+ unsigned int channel;
- if (amdtp_stream_running(&dice->rx_stream))
- return 0;
-
- err = amdtp_stream_start(&dice->rx_stream, dice->rx_resources.channel,
- fw_parent_device(dice->unit)->max_speed);
- if (err < 0)
- return err;
-
- err = snd_dice_transaction_set_enable(dice);
- if (err < 0) {
- amdtp_stream_stop(&dice->rx_stream);
- return err;
- }
+ /* Reset channel number */
+ channel = cpu_to_be32((u32)-1);
+ snd_dice_transaction_write_rx(dice, RX_ISOCHRONOUS, &channel, 4);
- return 0;
+ fw_iso_resources_free(&dice->rx_resources);
}
-int snd_dice_stream_start(struct snd_dice *dice)
+static int keep_resources(struct snd_dice *dice, unsigned int max_payload_bytes)
{
- __be32 channel;
+ unsigned int channel;
int err;
- if (!dice->rx_resources.allocated) {
- err = fw_iso_resources_allocate(&dice->rx_resources,
- amdtp_stream_get_max_payload(&dice->rx_stream),
+ err = fw_iso_resources_allocate(&dice->rx_resources, max_payload_bytes,
fw_parent_device(dice->unit)->max_speed);
- if (err < 0)
- goto error;
-
- channel = cpu_to_be32(dice->rx_resources.channel);
- err = snd_dice_transaction_write_tx(dice, RX_ISOCHRONOUS,
- &channel, 4);
- if (err < 0)
- goto err_resources;
- }
-
- err = snd_dice_stream_start_packets(dice);
if (err < 0)
- goto err_rx_channel;
-
- return 0;
+ goto end;
-err_rx_channel:
- channel = cpu_to_be32((u32)-1);
- snd_dice_transaction_write_rx(dice, RX_ISOCHRONOUS, &channel, 4);
-err_resources:
- fw_iso_resources_free(&dice->rx_resources);
-error:
+ /* Set channel number */
+ channel = cpu_to_be32(dice->rx_resources.channel);
+ err = snd_dice_transaction_write_rx(dice, RX_ISOCHRONOUS,
+ &channel, 4);
+ if (err < 0)
+ release_resources(dice);
+end:
return err;
}
-void snd_dice_stream_stop_packets(struct snd_dice *dice)
+static void stop_stream(struct snd_dice *dice)
{
if (!amdtp_stream_running(&dice->rx_stream))
return;
- snd_dice_transaction_clear_enable(dice);
+ amdtp_stream_pcm_abort(&dice->rx_stream);
amdtp_stream_stop(&dice->rx_stream);
+ release_resources(dice);
}
-void snd_dice_stream_stop(struct snd_dice *dice)
+static int start_stream(struct snd_dice *dice, unsigned int rate)
{
- __be32 channel;
+ unsigned int i, mode, pcm_chs, midi_ports;
+ int err;
- snd_dice_stream_stop_packets(dice);
+ err = snd_dice_stream_get_rate_mode(dice, rate, &mode);
+ if (err < 0)
+ goto end;
- if (!dice->rx_resources.allocated)
- return;
+ /*
+ * At 176.4/192.0 kHz, Dice has a quirk to transfer two PCM frames in
+ * one data block of AMDTP packet. Thus sampling transfer frequency is
+ * a half of PCM sampling frequency, i.e. PCM frames at 192.0 kHz are
+ * transferred on AMDTP packets at 96 kHz. Two successive samples of a
+ * channel are stored consecutively in the packet. This quirk is called
+ * as 'Dual Wire'.
+ * For this quirk, blocking mode is required and PCM buffer size should
+ * be aligned to SYT_INTERVAL.
+ */
+ pcm_chs = dice->rx_channels[mode];
+ midi_ports = dice->rx_midi_ports[mode];
+ if (mode > 1) {
+ rate /= 2;
+ pcm_chs *= 2;
+ dice->rx_stream.double_pcm_frames = true;
+ } else {
+ dice->rx_stream.double_pcm_frames = false;
+ }
- channel = cpu_to_be32((u32)-1);
- snd_dice_transaction_write_rx(dice, RX_ISOCHRONOUS, &channel, 4);
+ amdtp_stream_set_parameters(&dice->rx_stream, rate,
+ pcm_chs, midi_ports);
+ if (mode > 1) {
+ pcm_chs /= 2;
- fw_iso_resources_free(&dice->rx_resources);
+ for (i = 0; i < pcm_chs; i++) {
+ dice->rx_stream.pcm_positions[i] = i * 2;
+ dice->rx_stream.pcm_positions[i + pcm_chs] = i * 2 + 1;
+ }
+ }
+
+ err = keep_resources(dice,
+ amdtp_stream_get_max_payload(&dice->rx_stream));
+ if (err < 0) {
+ dev_err(&dice->unit->device,
+ "fail to keep isochronous resources\n");
+ goto end;
+ }
+
+ err = amdtp_stream_start(&dice->rx_stream, dice->rx_resources.channel,
+ fw_parent_device(dice->unit)->max_speed);
+ if (err < 0)
+ release_resources(dice);
+end:
+ return err;
+}
+
+int snd_dice_stream_start(struct snd_dice *dice, unsigned int rate)
+{
+ unsigned int curr_rate;
+ int err;
+
+ /* Some packet queueing errors. */
+ if (amdtp_streaming_error(&dice->rx_stream))
+ stop_stream(dice);
+
+ /* Stop stream if rate is different. */
+ err = snd_dice_transaction_get_rate(dice, &curr_rate);
+ if (err < 0) {
+ dev_err(&dice->unit->device,
+ "fail to get sampling rate\n");
+ goto end;
+ }
+ if (rate != curr_rate)
+ stop_stream(dice);
+
+ if (!amdtp_stream_running(&dice->rx_stream)) {
+ snd_dice_transaction_clear_enable(dice);
+
+ err = snd_dice_transaction_set_rate(dice, rate);
+ if (err < 0) {
+ dev_err(&dice->unit->device,
+ "fail to set sampling rate\n");
+ goto end;
+ }
+
+ /* Start stream. */
+ err = start_stream(dice, rate);
+ if (err < 0) {
+ dev_err(&dice->unit->device,
+ "fail to start AMDTP stream\n");
+ goto end;
+ }
+ err = snd_dice_transaction_set_enable(dice);
+ if (err < 0) {
+ dev_err(&dice->unit->device,
+ "fail to enable interface\n");
+ stop_stream(dice);
+ goto end;
+ }
+
+ if (!amdtp_stream_wait_callback(&dice->rx_stream,
+ CALLBACK_TIMEOUT)) {
+ snd_dice_transaction_clear_enable(dice);
+ stop_stream(dice);
+ err = -ETIMEDOUT;
+ }
+ }
+end:
+ return err;
+}
+
+void snd_dice_stream_stop(struct snd_dice *dice)
+{
+ snd_dice_transaction_clear_enable(dice);
+ stop_stream(dice);
}
int snd_dice_stream_init(struct snd_dice *dice)
void snd_dice_stream_destroy(struct snd_dice *dice)
{
- amdtp_stream_pcm_abort(&dice->rx_stream);
- snd_dice_stream_stop(dice);
+ snd_dice_transaction_clear_enable(dice);
+ stop_stream(dice);
amdtp_stream_destroy(&dice->rx_stream);
fw_iso_resources_destroy(&dice->rx_resources);
}
*/
dice->global_enabled = false;
- amdtp_stream_pcm_abort(&dice->rx_stream);
- snd_dice_stream_stop_packets(dice);
+ stop_stream(dice);
+
fw_iso_resources_update(&dice->rx_resources);
}