123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415 |
- /*
- * motu-stream.c - a part of driver for MOTU FireWire series
- *
- * Copyright (c) 2015-2017 Takashi Sakamoto <o-takashi@sakamocchi.jp>
- *
- * Licensed under the terms of the GNU General Public License, version 2.
- */
- #include "motu.h"
- #define CALLBACK_TIMEOUT 200
- #define ISOC_COMM_CONTROL_OFFSET 0x0b00
- #define ISOC_COMM_CONTROL_MASK 0xffff0000
- #define CHANGE_RX_ISOC_COMM_STATE 0x80000000
- #define RX_ISOC_COMM_IS_ACTIVATED 0x40000000
- #define RX_ISOC_COMM_CHANNEL_MASK 0x3f000000
- #define RX_ISOC_COMM_CHANNEL_SHIFT 24
- #define CHANGE_TX_ISOC_COMM_STATE 0x00800000
- #define TX_ISOC_COMM_IS_ACTIVATED 0x00400000
- #define TX_ISOC_COMM_CHANNEL_MASK 0x003f0000
- #define TX_ISOC_COMM_CHANNEL_SHIFT 16
- #define PACKET_FORMAT_OFFSET 0x0b10
- #define TX_PACKET_EXCLUDE_DIFFERED_DATA_CHUNKS 0x00000080
- #define RX_PACKET_EXCLUDE_DIFFERED_DATA_CHUNKS 0x00000040
- #define TX_PACKET_TRANSMISSION_SPEED_MASK 0x0000000f
- static int start_both_streams(struct snd_motu *motu, unsigned int rate)
- {
- unsigned int midi_ports = 0;
- __be32 reg;
- u32 data;
- int err;
- if ((motu->spec->flags & SND_MOTU_SPEC_RX_MIDI_2ND_Q) ||
- (motu->spec->flags & SND_MOTU_SPEC_RX_MIDI_3RD_Q))
- midi_ports = 1;
- /* Set packet formation to our packet streaming engine. */
- err = amdtp_motu_set_parameters(&motu->rx_stream, rate, midi_ports,
- &motu->rx_packet_formats);
- if (err < 0)
- return err;
- if ((motu->spec->flags & SND_MOTU_SPEC_TX_MIDI_2ND_Q) ||
- (motu->spec->flags & SND_MOTU_SPEC_TX_MIDI_3RD_Q))
- midi_ports = 1;
- else
- midi_ports = 0;
- err = amdtp_motu_set_parameters(&motu->tx_stream, rate, midi_ports,
- &motu->tx_packet_formats);
- if (err < 0)
- return err;
- /* Get isochronous resources on the bus. */
- err = fw_iso_resources_allocate(&motu->rx_resources,
- amdtp_stream_get_max_payload(&motu->rx_stream),
- fw_parent_device(motu->unit)->max_speed);
- if (err < 0)
- return err;
- err = fw_iso_resources_allocate(&motu->tx_resources,
- amdtp_stream_get_max_payload(&motu->tx_stream),
- fw_parent_device(motu->unit)->max_speed);
- if (err < 0)
- return err;
- /* Configure the unit to start isochronous communication. */
- err = snd_motu_transaction_read(motu, ISOC_COMM_CONTROL_OFFSET, ®,
- sizeof(reg));
- if (err < 0)
- return err;
- data = be32_to_cpu(reg) & ~ISOC_COMM_CONTROL_MASK;
- data |= CHANGE_RX_ISOC_COMM_STATE | RX_ISOC_COMM_IS_ACTIVATED |
- (motu->rx_resources.channel << RX_ISOC_COMM_CHANNEL_SHIFT) |
- CHANGE_TX_ISOC_COMM_STATE | TX_ISOC_COMM_IS_ACTIVATED |
- (motu->tx_resources.channel << TX_ISOC_COMM_CHANNEL_SHIFT);
- reg = cpu_to_be32(data);
- return snd_motu_transaction_write(motu, ISOC_COMM_CONTROL_OFFSET, ®,
- sizeof(reg));
- }
- static void stop_both_streams(struct snd_motu *motu)
- {
- __be32 reg;
- u32 data;
- int err;
- err = motu->spec->protocol->switch_fetching_mode(motu, false);
- if (err < 0)
- return;
- err = snd_motu_transaction_read(motu, ISOC_COMM_CONTROL_OFFSET, ®,
- sizeof(reg));
- if (err < 0)
- return;
- data = be32_to_cpu(reg);
- data &= ~(RX_ISOC_COMM_IS_ACTIVATED | TX_ISOC_COMM_IS_ACTIVATED);
- data |= CHANGE_RX_ISOC_COMM_STATE | CHANGE_TX_ISOC_COMM_STATE;
- reg = cpu_to_be32(data);
- snd_motu_transaction_write(motu, ISOC_COMM_CONTROL_OFFSET, ®,
- sizeof(reg));
- fw_iso_resources_free(&motu->tx_resources);
- fw_iso_resources_free(&motu->rx_resources);
- }
- static int start_isoc_ctx(struct snd_motu *motu, struct amdtp_stream *stream)
- {
- struct fw_iso_resources *resources;
- int err;
- if (stream == &motu->rx_stream)
- resources = &motu->rx_resources;
- else
- resources = &motu->tx_resources;
- err = amdtp_stream_start(stream, resources->channel,
- fw_parent_device(motu->unit)->max_speed);
- if (err < 0)
- return err;
- if (!amdtp_stream_wait_callback(stream, CALLBACK_TIMEOUT)) {
- amdtp_stream_stop(stream);
- fw_iso_resources_free(resources);
- return -ETIMEDOUT;
- }
- return 0;
- }
- static void stop_isoc_ctx(struct snd_motu *motu, struct amdtp_stream *stream)
- {
- struct fw_iso_resources *resources;
- if (stream == &motu->rx_stream)
- resources = &motu->rx_resources;
- else
- resources = &motu->tx_resources;
- amdtp_stream_stop(stream);
- fw_iso_resources_free(resources);
- }
- int snd_motu_stream_cache_packet_formats(struct snd_motu *motu)
- {
- int err;
- err = motu->spec->protocol->cache_packet_formats(motu);
- if (err < 0)
- return err;
- if (motu->spec->flags & SND_MOTU_SPEC_TX_MIDI_2ND_Q) {
- motu->tx_packet_formats.midi_flag_offset = 4;
- motu->tx_packet_formats.midi_byte_offset = 6;
- } else if (motu->spec->flags & SND_MOTU_SPEC_TX_MIDI_3RD_Q) {
- motu->tx_packet_formats.midi_flag_offset = 8;
- motu->tx_packet_formats.midi_byte_offset = 7;
- }
- if (motu->spec->flags & SND_MOTU_SPEC_RX_MIDI_2ND_Q) {
- motu->rx_packet_formats.midi_flag_offset = 4;
- motu->rx_packet_formats.midi_byte_offset = 6;
- } else if (motu->spec->flags & SND_MOTU_SPEC_RX_MIDI_3RD_Q) {
- motu->rx_packet_formats.midi_flag_offset = 8;
- motu->rx_packet_formats.midi_byte_offset = 7;
- }
- return 0;
- }
- static int ensure_packet_formats(struct snd_motu *motu)
- {
- __be32 reg;
- u32 data;
- int err;
- err = snd_motu_transaction_read(motu, PACKET_FORMAT_OFFSET, ®,
- sizeof(reg));
- if (err < 0)
- return err;
- data = be32_to_cpu(reg);
- data &= ~(TX_PACKET_EXCLUDE_DIFFERED_DATA_CHUNKS |
- RX_PACKET_EXCLUDE_DIFFERED_DATA_CHUNKS|
- TX_PACKET_TRANSMISSION_SPEED_MASK);
- if (motu->tx_packet_formats.differed_part_pcm_chunks[0] == 0)
- data |= TX_PACKET_EXCLUDE_DIFFERED_DATA_CHUNKS;
- if (motu->rx_packet_formats.differed_part_pcm_chunks[0] == 0)
- data |= RX_PACKET_EXCLUDE_DIFFERED_DATA_CHUNKS;
- data |= fw_parent_device(motu->unit)->max_speed;
- reg = cpu_to_be32(data);
- return snd_motu_transaction_write(motu, PACKET_FORMAT_OFFSET, ®,
- sizeof(reg));
- }
- int snd_motu_stream_start_duplex(struct snd_motu *motu, unsigned int rate)
- {
- const struct snd_motu_protocol *protocol = motu->spec->protocol;
- unsigned int curr_rate;
- int err = 0;
- if (motu->capture_substreams == 0 && motu->playback_substreams == 0)
- return 0;
- /* Some packet queueing errors. */
- if (amdtp_streaming_error(&motu->rx_stream) ||
- amdtp_streaming_error(&motu->tx_stream)) {
- amdtp_stream_stop(&motu->rx_stream);
- amdtp_stream_stop(&motu->tx_stream);
- stop_both_streams(motu);
- }
- err = snd_motu_stream_cache_packet_formats(motu);
- if (err < 0)
- return err;
- /* Stop stream if rate is different. */
- err = protocol->get_clock_rate(motu, &curr_rate);
- if (err < 0) {
- dev_err(&motu->unit->device,
- "fail to get sampling rate: %d\n", err);
- return err;
- }
- if (rate == 0)
- rate = curr_rate;
- if (rate != curr_rate) {
- amdtp_stream_stop(&motu->rx_stream);
- amdtp_stream_stop(&motu->tx_stream);
- stop_both_streams(motu);
- }
- if (!amdtp_stream_running(&motu->rx_stream)) {
- err = protocol->set_clock_rate(motu, rate);
- if (err < 0) {
- dev_err(&motu->unit->device,
- "fail to set sampling rate: %d\n", err);
- return err;
- }
- err = ensure_packet_formats(motu);
- if (err < 0)
- return err;
- err = start_both_streams(motu, rate);
- if (err < 0) {
- dev_err(&motu->unit->device,
- "fail to start isochronous comm: %d\n", err);
- goto stop_streams;
- }
- err = start_isoc_ctx(motu, &motu->rx_stream);
- if (err < 0) {
- dev_err(&motu->unit->device,
- "fail to start IT context: %d\n", err);
- goto stop_streams;
- }
- err = protocol->switch_fetching_mode(motu, true);
- if (err < 0) {
- dev_err(&motu->unit->device,
- "fail to enable frame fetching: %d\n", err);
- goto stop_streams;
- }
- }
- if (!amdtp_stream_running(&motu->tx_stream) &&
- motu->capture_substreams > 0) {
- err = start_isoc_ctx(motu, &motu->tx_stream);
- if (err < 0) {
- dev_err(&motu->unit->device,
- "fail to start IR context: %d", err);
- amdtp_stream_stop(&motu->rx_stream);
- goto stop_streams;
- }
- }
- return 0;
- stop_streams:
- stop_both_streams(motu);
- return err;
- }
- void snd_motu_stream_stop_duplex(struct snd_motu *motu)
- {
- if (motu->capture_substreams == 0) {
- if (amdtp_stream_running(&motu->tx_stream))
- stop_isoc_ctx(motu, &motu->tx_stream);
- if (motu->playback_substreams == 0) {
- if (amdtp_stream_running(&motu->rx_stream))
- stop_isoc_ctx(motu, &motu->rx_stream);
- stop_both_streams(motu);
- }
- }
- }
- static int init_stream(struct snd_motu *motu, enum amdtp_stream_direction dir)
- {
- int err;
- struct amdtp_stream *stream;
- struct fw_iso_resources *resources;
- if (dir == AMDTP_IN_STREAM) {
- stream = &motu->tx_stream;
- resources = &motu->tx_resources;
- } else {
- stream = &motu->rx_stream;
- resources = &motu->rx_resources;
- }
- err = fw_iso_resources_init(resources, motu->unit);
- if (err < 0)
- return err;
- err = amdtp_motu_init(stream, motu->unit, dir, motu->spec->protocol);
- if (err < 0) {
- amdtp_stream_destroy(stream);
- fw_iso_resources_destroy(resources);
- }
- return err;
- }
- static void destroy_stream(struct snd_motu *motu,
- enum amdtp_stream_direction dir)
- {
- struct amdtp_stream *stream;
- struct fw_iso_resources *resources;
- if (dir == AMDTP_IN_STREAM) {
- stream = &motu->tx_stream;
- resources = &motu->tx_resources;
- } else {
- stream = &motu->rx_stream;
- resources = &motu->rx_resources;
- }
- amdtp_stream_destroy(stream);
- fw_iso_resources_destroy(resources);
- }
- int snd_motu_stream_init_duplex(struct snd_motu *motu)
- {
- int err;
- err = init_stream(motu, AMDTP_IN_STREAM);
- if (err < 0)
- return err;
- err = init_stream(motu, AMDTP_OUT_STREAM);
- if (err < 0)
- destroy_stream(motu, AMDTP_IN_STREAM);
- return err;
- }
- /*
- * This function should be called before starting streams or after stopping
- * streams.
- */
- void snd_motu_stream_destroy_duplex(struct snd_motu *motu)
- {
- destroy_stream(motu, AMDTP_IN_STREAM);
- destroy_stream(motu, AMDTP_OUT_STREAM);
- motu->playback_substreams = 0;
- motu->capture_substreams = 0;
- }
- static void motu_lock_changed(struct snd_motu *motu)
- {
- motu->dev_lock_changed = true;
- wake_up(&motu->hwdep_wait);
- }
- int snd_motu_stream_lock_try(struct snd_motu *motu)
- {
- int err;
- spin_lock_irq(&motu->lock);
- if (motu->dev_lock_count < 0) {
- err = -EBUSY;
- goto out;
- }
- if (motu->dev_lock_count++ == 0)
- motu_lock_changed(motu);
- err = 0;
- out:
- spin_unlock_irq(&motu->lock);
- return err;
- }
- void snd_motu_stream_lock_release(struct snd_motu *motu)
- {
- spin_lock_irq(&motu->lock);
- if (WARN_ON(motu->dev_lock_count <= 0))
- goto out;
- if (--motu->dev_lock_count == 0)
- motu_lock_changed(motu);
- out:
- spin_unlock_irq(&motu->lock);
- }
|