diff options
Diffstat (limited to 'sound/firewire/dice/dice-stream.c')
-rw-r--r-- | sound/firewire/dice/dice-stream.c | 230 |
1 files changed, 168 insertions, 62 deletions
diff --git a/sound/firewire/dice/dice-stream.c b/sound/firewire/dice/dice-stream.c index b9d7a4846459..e60b84d7a0f6 100644 --- a/sound/firewire/dice/dice-stream.c +++ b/sound/firewire/dice/dice-stream.c @@ -41,55 +41,79 @@ int snd_dice_stream_get_rate_mode(struct snd_dice *dice, unsigned int rate, return -EINVAL; } -static void release_resources(struct snd_dice *dice) +static void release_resources(struct snd_dice *dice, + struct fw_iso_resources *resources) { unsigned int channel; /* Reset channel number */ channel = cpu_to_be32((u32)-1); - snd_dice_transaction_write_rx(dice, RX_ISOCHRONOUS, &channel, 4); - - fw_iso_resources_free(&dice->rx_resources); + if (resources == &dice->tx_resources) + snd_dice_transaction_write_tx(dice, TX_ISOCHRONOUS, + &channel, 4); + else + snd_dice_transaction_write_rx(dice, RX_ISOCHRONOUS, + &channel, 4); + + fw_iso_resources_free(resources); } -static int keep_resources(struct snd_dice *dice, unsigned int max_payload_bytes) +static int keep_resources(struct snd_dice *dice, + struct fw_iso_resources *resources, + unsigned int max_payload_bytes) { unsigned int channel; int err; - err = fw_iso_resources_allocate(&dice->rx_resources, max_payload_bytes, + err = fw_iso_resources_allocate(resources, max_payload_bytes, fw_parent_device(dice->unit)->max_speed); if (err < 0) goto end; /* Set channel number */ - channel = cpu_to_be32(dice->rx_resources.channel); - err = snd_dice_transaction_write_rx(dice, RX_ISOCHRONOUS, - &channel, 4); + channel = cpu_to_be32(resources->channel); + if (resources == &dice->tx_resources) + err = snd_dice_transaction_write_tx(dice, TX_ISOCHRONOUS, + &channel, 4); + else + err = snd_dice_transaction_write_rx(dice, RX_ISOCHRONOUS, + &channel, 4); if (err < 0) - release_resources(dice); + release_resources(dice, resources); end: return err; } -static void stop_stream(struct snd_dice *dice) +static void stop_stream(struct snd_dice *dice, struct amdtp_stream *stream) { - if (!amdtp_stream_running(&dice->rx_stream)) - return; + amdtp_stream_pcm_abort(stream); + amdtp_stream_stop(stream); - amdtp_stream_pcm_abort(&dice->rx_stream); - amdtp_stream_stop(&dice->rx_stream); - release_resources(dice); + if (stream == &dice->tx_stream) + release_resources(dice, &dice->tx_resources); + else + release_resources(dice, &dice->rx_resources); } -static int start_stream(struct snd_dice *dice, unsigned int rate) +static int start_stream(struct snd_dice *dice, struct amdtp_stream *stream, + unsigned int rate) { + struct fw_iso_resources *resources; unsigned int i, mode, pcm_chs, midi_ports; int err; err = snd_dice_stream_get_rate_mode(dice, rate, &mode); if (err < 0) goto end; + if (stream == &dice->tx_stream) { + resources = &dice->tx_resources; + pcm_chs = dice->tx_channels[mode]; + midi_ports = dice->tx_midi_ports[mode]; + } else { + resources = &dice->rx_resources; + pcm_chs = dice->rx_channels[mode]; + midi_ports = dice->rx_midi_ports[mode]; + } /* * At 176.4/192.0 kHz, Dice has a quirk to transfer two PCM frames in @@ -101,51 +125,71 @@ static int start_stream(struct snd_dice *dice, unsigned int rate) * 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; + stream->double_pcm_frames = true; } else { - dice->rx_stream.double_pcm_frames = false; + stream->double_pcm_frames = false; } - amdtp_stream_set_parameters(&dice->rx_stream, rate, - pcm_chs, midi_ports); + amdtp_stream_set_parameters(stream, rate, pcm_chs, midi_ports); if (mode > 1) { pcm_chs /= 2; 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; + stream->pcm_positions[i] = i * 2; + stream->pcm_positions[i + pcm_chs] = i * 2 + 1; } } - err = keep_resources(dice, - amdtp_stream_get_max_payload(&dice->rx_stream)); + err = keep_resources(dice, resources, + amdtp_stream_get_max_payload(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, + err = amdtp_stream_start(stream, resources->channel, fw_parent_device(dice->unit)->max_speed); if (err < 0) - release_resources(dice); + release_resources(dice, resources); end: return err; } -int snd_dice_stream_start(struct snd_dice *dice, unsigned int rate) +static int get_sync_mode(struct snd_dice *dice, enum cip_flags *sync_mode) +{ + /* Currently, clock source is fixed at SYT-Match mode. */ + *sync_mode = 0; + return 0; +} + +int snd_dice_stream_start_duplex(struct snd_dice *dice, unsigned int rate) { + struct amdtp_stream *master, *slave; unsigned int curr_rate; - int err; + enum cip_flags sync_mode; + int err = 0; + + if (dice->substreams_counter == 0) + goto end; + + err = get_sync_mode(dice, &sync_mode); + if (err < 0) + goto end; + if (sync_mode == CIP_SYNC_TO_DEVICE) { + master = &dice->tx_stream; + slave = &dice->rx_stream; + } else { + master = &dice->rx_stream; + slave = &dice->tx_stream; + } /* Some packet queueing errors. */ - if (amdtp_streaming_error(&dice->rx_stream)) - stop_stream(dice); + if (amdtp_streaming_error(master) || amdtp_streaming_error(slave)) + stop_stream(dice, master); /* Stop stream if rate is different. */ err = snd_dice_transaction_get_rate(dice, &curr_rate); @@ -155,11 +199,14 @@ int snd_dice_stream_start(struct snd_dice *dice, unsigned int rate) goto end; } if (rate != curr_rate) - stop_stream(dice); + stop_stream(dice, master); - if (!amdtp_stream_running(&dice->rx_stream)) { + if (!amdtp_stream_running(master)) { + stop_stream(dice, slave); snd_dice_transaction_clear_enable(dice); + amdtp_stream_set_sync(sync_mode, master, slave); + err = snd_dice_transaction_set_rate(dice, rate); if (err < 0) { dev_err(&dice->unit->device, @@ -167,25 +214,35 @@ int snd_dice_stream_start(struct snd_dice *dice, unsigned int rate) goto end; } - /* Start stream. */ - err = start_stream(dice, rate); + /* Start both streams. */ + err = start_stream(dice, master, rate); + if (err < 0) { + dev_err(&dice->unit->device, + "fail to start AMDTP master stream\n"); + goto end; + } + err = start_stream(dice, slave, rate); if (err < 0) { dev_err(&dice->unit->device, - "fail to start AMDTP stream\n"); + "fail to start AMDTP slave stream\n"); + stop_stream(dice, master); 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); + stop_stream(dice, master); + stop_stream(dice, slave); goto end; } - if (!amdtp_stream_wait_callback(&dice->rx_stream, - CALLBACK_TIMEOUT)) { + /* Wait first callbacks */ + if (!amdtp_stream_wait_callback(master, CALLBACK_TIMEOUT) || + !amdtp_stream_wait_callback(slave, CALLBACK_TIMEOUT)) { snd_dice_transaction_clear_enable(dice); - stop_stream(dice); + stop_stream(dice, master); + stop_stream(dice, slave); err = -ETIMEDOUT; } } @@ -193,46 +250,93 @@ end: return err; } -void snd_dice_stream_stop(struct snd_dice *dice) +void snd_dice_stream_stop_duplex(struct snd_dice *dice) { + if (dice->substreams_counter > 0) + return; + snd_dice_transaction_clear_enable(dice); - stop_stream(dice); + + stop_stream(dice, &dice->tx_stream); + stop_stream(dice, &dice->rx_stream); } -int snd_dice_stream_init(struct snd_dice *dice) +static int init_stream(struct snd_dice *dice, struct amdtp_stream *stream) { int err; + struct fw_iso_resources *resources; + enum amdtp_stream_direction dir; + + if (stream == &dice->tx_stream) { + resources = &dice->tx_resources; + dir = AMDTP_IN_STREAM; + } else { + resources = &dice->rx_resources; + dir = AMDTP_OUT_STREAM; + } - err = fw_iso_resources_init(&dice->rx_resources, dice->unit); + err = fw_iso_resources_init(resources, dice->unit); if (err < 0) goto end; - dice->rx_resources.channels_mask = 0x00000000ffffffffuLL; + resources->channels_mask = 0x00000000ffffffffuLL; - err = amdtp_stream_init(&dice->rx_stream, dice->unit, AMDTP_OUT_STREAM, - CIP_BLOCKING); + err = amdtp_stream_init(stream, dice->unit, dir, CIP_BLOCKING); + if (err < 0) { + amdtp_stream_destroy(stream); + fw_iso_resources_destroy(resources); + } +end: + return err; +} + +static void destroy_stream(struct snd_dice *dice, struct amdtp_stream *stream) +{ + amdtp_stream_destroy(stream); + + if (stream == &dice->tx_stream) + fw_iso_resources_destroy(&dice->tx_resources); + else + fw_iso_resources_destroy(&dice->rx_resources); +} + +int snd_dice_stream_init_duplex(struct snd_dice *dice) +{ + int err; + + dice->substreams_counter = 0; + + err = init_stream(dice, &dice->tx_stream); if (err < 0) - goto error; + goto end; - err = snd_dice_transaction_set_clock_source(dice, CLOCK_SOURCE_ARX1); + err = init_stream(dice, &dice->rx_stream); if (err < 0) - goto error; + goto end; + + /* Currently, clock source is fixed at SYT-Match mode. */ + err = snd_dice_transaction_set_clock_source(dice, CLOCK_SOURCE_ARX1); + if (err < 0) { + destroy_stream(dice, &dice->rx_stream); + destroy_stream(dice, &dice->tx_stream); + } end: return err; -error: - amdtp_stream_destroy(&dice->rx_stream); - fw_iso_resources_destroy(&dice->rx_resources); - return err; } -void snd_dice_stream_destroy(struct snd_dice *dice) +void snd_dice_stream_destroy_duplex(struct snd_dice *dice) { snd_dice_transaction_clear_enable(dice); - stop_stream(dice); - amdtp_stream_destroy(&dice->rx_stream); - fw_iso_resources_destroy(&dice->rx_resources); + + stop_stream(dice, &dice->tx_stream); + destroy_stream(dice, &dice->tx_stream); + + stop_stream(dice, &dice->rx_stream); + destroy_stream(dice, &dice->rx_stream); + + dice->substreams_counter = 0; } -void snd_dice_stream_update(struct snd_dice *dice) +void snd_dice_stream_update_duplex(struct snd_dice *dice) { /* * On a bus reset, the DICE firmware disables streaming and then goes @@ -244,9 +348,11 @@ void snd_dice_stream_update(struct snd_dice *dice) */ dice->global_enabled = false; - stop_stream(dice); + stop_stream(dice, &dice->rx_stream); + stop_stream(dice, &dice->tx_stream); fw_iso_resources_update(&dice->rx_resources); + fw_iso_resources_update(&dice->tx_resources); } static void dice_lock_changed(struct snd_dice *dice) |