diff options
Diffstat (limited to 'sound/firewire/fireface')
-rw-r--r-- | sound/firewire/fireface/ff-pcm.c | 56 | ||||
-rw-r--r-- | sound/firewire/fireface/ff-protocol-former.c | 112 | ||||
-rw-r--r-- | sound/firewire/fireface/ff-protocol-latter.c | 114 | ||||
-rw-r--r-- | sound/firewire/fireface/ff-stream.c | 79 | ||||
-rw-r--r-- | sound/firewire/fireface/ff.h | 2 |
5 files changed, 176 insertions, 187 deletions
diff --git a/sound/firewire/fireface/ff-pcm.c b/sound/firewire/fireface/ff-pcm.c index 0d40bb68db50..9eab3ad283ce 100644 --- a/sound/firewire/fireface/ff-pcm.c +++ b/sound/firewire/fireface/ff-pcm.c @@ -198,8 +198,8 @@ static int pcm_close(struct snd_pcm_substream *substream) return 0; } -static int pcm_capture_hw_params(struct snd_pcm_substream *substream, - struct snd_pcm_hw_params *hw_params) +static int pcm_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *hw_params) { struct snd_ff *ff = substream->private_data; int err; @@ -210,58 +210,26 @@ static int pcm_capture_hw_params(struct snd_pcm_substream *substream, return err; if (substream->runtime->status->state == SNDRV_PCM_STATE_OPEN) { - mutex_lock(&ff->mutex); - ff->substreams_counter++; - mutex_unlock(&ff->mutex); - } - - return 0; -} - -static int pcm_playback_hw_params(struct snd_pcm_substream *substream, - struct snd_pcm_hw_params *hw_params) -{ - struct snd_ff *ff = substream->private_data; - int err; - - err = snd_pcm_lib_alloc_vmalloc_buffer(substream, - params_buffer_bytes(hw_params)); - if (err < 0) - return err; + unsigned int rate = params_rate(hw_params); - if (substream->runtime->status->state == SNDRV_PCM_STATE_OPEN) { mutex_lock(&ff->mutex); - ff->substreams_counter++; + err = snd_ff_stream_reserve_duplex(ff, rate); + if (err >= 0) + ++ff->substreams_counter; mutex_unlock(&ff->mutex); } return 0; } -static int pcm_capture_hw_free(struct snd_pcm_substream *substream) -{ - struct snd_ff *ff = substream->private_data; - - mutex_lock(&ff->mutex); - - if (substream->runtime->status->state != SNDRV_PCM_STATE_OPEN) - ff->substreams_counter--; - - snd_ff_stream_stop_duplex(ff); - - mutex_unlock(&ff->mutex); - - return snd_pcm_lib_free_vmalloc_buffer(substream); -} - -static int pcm_playback_hw_free(struct snd_pcm_substream *substream) +static int pcm_hw_free(struct snd_pcm_substream *substream) { struct snd_ff *ff = substream->private_data; mutex_lock(&ff->mutex); if (substream->runtime->status->state != SNDRV_PCM_STATE_OPEN) - ff->substreams_counter--; + --ff->substreams_counter; snd_ff_stream_stop_duplex(ff); @@ -374,8 +342,8 @@ int snd_ff_create_pcm_devices(struct snd_ff *ff) .open = pcm_open, .close = pcm_close, .ioctl = snd_pcm_lib_ioctl, - .hw_params = pcm_capture_hw_params, - .hw_free = pcm_capture_hw_free, + .hw_params = pcm_hw_params, + .hw_free = pcm_hw_free, .prepare = pcm_capture_prepare, .trigger = pcm_capture_trigger, .pointer = pcm_capture_pointer, @@ -386,8 +354,8 @@ int snd_ff_create_pcm_devices(struct snd_ff *ff) .open = pcm_open, .close = pcm_close, .ioctl = snd_pcm_lib_ioctl, - .hw_params = pcm_playback_hw_params, - .hw_free = pcm_playback_hw_free, + .hw_params = pcm_hw_params, + .hw_free = pcm_hw_free, .prepare = pcm_playback_prepare, .trigger = pcm_playback_trigger, .pointer = pcm_playback_pointer, diff --git a/sound/firewire/fireface/ff-protocol-former.c b/sound/firewire/fireface/ff-protocol-former.c index 8d1c2c6e907b..bf44cad7985e 100644 --- a/sound/firewire/fireface/ff-protocol-former.c +++ b/sound/firewire/fireface/ff-protocol-former.c @@ -293,27 +293,6 @@ static int former_fill_midi_msg(struct snd_ff *ff, #define FF800_TX_PACKET_ISOC_CH 0x0000801c0008 -static int allocate_rx_resources(struct snd_ff *ff) -{ - u32 data; - __le32 reg; - int err; - - // Controllers should allocate isochronous resources for rx stream. - err = fw_iso_resources_allocate(&ff->rx_resources, - amdtp_stream_get_max_payload(&ff->rx_stream), - fw_parent_device(ff->unit)->max_speed); - if (err < 0) - return err; - - // Set isochronous channel and the number of quadlets of rx packets. - data = ff->rx_stream.data_block_quadlets << 3; - data = (data << 8) | ff->rx_resources.channel; - reg = cpu_to_le32(data); - return snd_fw_transaction(ff->unit, TCODE_WRITE_QUADLET_REQUEST, - FF800_RX_PACKET_FORMAT, ®, sizeof(reg), 0); -} - static int allocate_tx_resources(struct snd_ff *ff) { __le32 reg; @@ -355,8 +334,9 @@ static int allocate_tx_resources(struct snd_ff *ff) return 0; } -static int ff800_begin_session(struct snd_ff *ff, unsigned int rate) +static int ff800_allocate_resources(struct snd_ff *ff, unsigned int rate) { + u32 data; __le32 reg; int err; @@ -371,14 +351,38 @@ static int ff800_begin_session(struct snd_ff *ff, unsigned int rate) // Let's sleep for a bit. msleep(100); - err = allocate_rx_resources(ff); + // Controllers should allocate isochronous resources for rx stream. + err = fw_iso_resources_allocate(&ff->rx_resources, + amdtp_stream_get_max_payload(&ff->rx_stream), + fw_parent_device(ff->unit)->max_speed); if (err < 0) return err; - err = allocate_tx_resources(ff); + // Set isochronous channel and the number of quadlets of rx packets. + // This should be done before the allocation of tx resources to avoid + // periodical noise. + data = ff->rx_stream.data_block_quadlets << 3; + data = (data << 8) | ff->rx_resources.channel; + reg = cpu_to_le32(data); + err = snd_fw_transaction(ff->unit, TCODE_WRITE_QUADLET_REQUEST, + FF800_RX_PACKET_FORMAT, ®, sizeof(reg), 0); if (err < 0) return err; + return allocate_tx_resources(ff); +} + +static int ff800_begin_session(struct snd_ff *ff, unsigned int rate) +{ + unsigned int generation = ff->rx_resources.generation; + __le32 reg; + + if (generation != fw_parent_device(ff->unit)->card->generation) { + int err = fw_iso_resources_update(&ff->rx_resources); + if (err < 0) + return err; + } + reg = cpu_to_le32(0x80000000); reg |= cpu_to_le32(ff->tx_stream.data_block_quadlets); if (fw_parent_device(ff->unit)->max_speed == SCODE_800) @@ -420,6 +424,7 @@ const struct snd_ff_protocol snd_ff_protocol_ff800 = { .fill_midi_msg = former_fill_midi_msg, .get_clock = former_get_clock, .switch_fetching_mode = former_switch_fetching_mode, + .allocate_resources = ff800_allocate_resources, .begin_session = ff800_begin_session, .finish_session = ff800_finish_session, .dump_status = former_dump_status, @@ -431,12 +436,11 @@ const struct snd_ff_protocol snd_ff_protocol_ff800 = { #define FF400_TX_PACKET_FORMAT 0x00008010050cull #define FF400_ISOC_COMM_STOP 0x000080100510ull -/* - * Fireface 400 manages isochronous channel number in 3 bit field. Therefore, - * we can allocate between 0 and 7 channel. - */ -static int keep_resources(struct snd_ff *ff, unsigned int rate) +// Fireface 400 manages isochronous channel number in 3 bit field. Therefore, +// we can allocate between 0 and 7 channel. +static int ff400_allocate_resources(struct snd_ff *ff, unsigned int rate) { + __le32 reg; enum snd_ff_stream_mode mode; int i; int err; @@ -449,11 +453,20 @@ static int keep_resources(struct snd_ff *ff, unsigned int rate) if (i >= CIP_SFC_COUNT) return -EINVAL; + // Set the number of data blocks transferred in a second. + reg = cpu_to_le32(rate); + err = snd_fw_transaction(ff->unit, TCODE_WRITE_QUADLET_REQUEST, + FF400_STF, ®, sizeof(reg), 0); + if (err < 0) + return err; + + msleep(100); + err = snd_ff_stream_get_multiplier_mode(i, &mode); if (err < 0) return err; - /* Keep resources for in-stream. */ + // Keep resources for in-stream. ff->tx_resources.channels_mask = 0x00000000000000ffuLL; err = fw_iso_resources_allocate(&ff->tx_resources, amdtp_stream_get_max_payload(&ff->tx_stream), @@ -461,7 +474,7 @@ static int keep_resources(struct snd_ff *ff, unsigned int rate) if (err < 0) return err; - /* Keep resources for out-stream. */ + // Keep resources for out-stream. ff->rx_resources.channels_mask = 0x00000000000000ffuLL; err = fw_iso_resources_allocate(&ff->rx_resources, amdtp_stream_get_max_payload(&ff->rx_stream), @@ -474,26 +487,22 @@ static int keep_resources(struct snd_ff *ff, unsigned int rate) static int ff400_begin_session(struct snd_ff *ff, unsigned int rate) { + unsigned int generation = ff->rx_resources.generation; __le32 reg; int err; - err = keep_resources(ff, rate); - if (err < 0) - return err; - - /* Set the number of data blocks transferred in a second. */ - reg = cpu_to_le32(rate); - err = snd_fw_transaction(ff->unit, TCODE_WRITE_QUADLET_REQUEST, - FF400_STF, ®, sizeof(reg), 0); - if (err < 0) - return err; + if (generation != fw_parent_device(ff->unit)->card->generation) { + err = fw_iso_resources_update(&ff->tx_resources); + if (err < 0) + return err; - msleep(100); + err = fw_iso_resources_update(&ff->rx_resources); + if (err < 0) + return err; + } - /* - * Set isochronous channel and the number of quadlets of received - * packets. - */ + // Set isochronous channel and the number of quadlets of received + // packets. reg = cpu_to_le32(((ff->rx_stream.data_block_quadlets << 3) << 8) | ff->rx_resources.channel); err = snd_fw_transaction(ff->unit, TCODE_WRITE_QUADLET_REQUEST, @@ -501,11 +510,9 @@ static int ff400_begin_session(struct snd_ff *ff, unsigned int rate) if (err < 0) return err; - /* - * Set isochronous channel and the number of quadlets of transmitted - * packet. - */ - /* TODO: investigate the purpose of this 0x80. */ + // Set isochronous channel and the number of quadlets of transmitted + // packet. + // TODO: investigate the purpose of this 0x80. reg = cpu_to_le32((0x80 << 24) | (ff->tx_resources.channel << 5) | (ff->tx_stream.data_block_quadlets)); @@ -514,7 +521,7 @@ static int ff400_begin_session(struct snd_ff *ff, unsigned int rate) if (err < 0) return err; - /* Allow to transmit packets. */ + // Allow to transmit packets. reg = cpu_to_le32(0x00000001); return snd_fw_transaction(ff->unit, TCODE_WRITE_QUADLET_REQUEST, FF400_ISOC_COMM_START, ®, sizeof(reg), 0); @@ -591,6 +598,7 @@ const struct snd_ff_protocol snd_ff_protocol_ff400 = { .fill_midi_msg = former_fill_midi_msg, .get_clock = former_get_clock, .switch_fetching_mode = former_switch_fetching_mode, + .allocate_resources = ff400_allocate_resources, .begin_session = ff400_begin_session, .finish_session = ff400_finish_session, .dump_status = former_dump_status, diff --git a/sound/firewire/fireface/ff-protocol-latter.c b/sound/firewire/fireface/ff-protocol-latter.c index b30d02d359b1..0e4c3a9ed5e4 100644 --- a/sound/firewire/fireface/ff-protocol-latter.c +++ b/sound/firewire/fireface/ff-protocol-latter.c @@ -97,25 +97,64 @@ static int latter_switch_fetching_mode(struct snd_ff *ff, bool enable) LATTER_FETCH_MODE, ®, sizeof(reg), 0); } -static int keep_resources(struct snd_ff *ff, unsigned int rate) +static int latter_allocate_resources(struct snd_ff *ff, unsigned int rate) { enum snd_ff_stream_mode mode; + unsigned int code; + __le32 reg; + unsigned int count; int i; int err; - // Check whether the given value is supported or not. - for (i = 0; i < CIP_SFC_COUNT; i++) { - if (amdtp_rate_table[i] == rate) + // Set the number of data blocks transferred in a second. + if (rate % 32000 == 0) + code = 0x00; + else if (rate % 44100 == 0) + code = 0x02; + else if (rate % 48000 == 0) + code = 0x04; + else + return -EINVAL; + + if (rate >= 64000 && rate < 128000) + code |= 0x08; + else if (rate >= 128000 && rate < 192000) + code |= 0x10; + + reg = cpu_to_le32(code); + err = snd_fw_transaction(ff->unit, TCODE_WRITE_QUADLET_REQUEST, + LATTER_STF, ®, sizeof(reg), 0); + if (err < 0) + return err; + + // Confirm to shift transmission clock. + count = 0; + while (count++ < 10) { + unsigned int curr_rate; + enum snd_ff_clock_src src; + + err = latter_get_clock(ff, &curr_rate, &src); + if (err < 0) + return err; + + if (curr_rate == rate) break; } - if (i >= CIP_SFC_COUNT) + if (count == 10) + return -ETIMEDOUT; + + for (i = 0; i < ARRAY_SIZE(amdtp_rate_table); ++i) { + if (rate == amdtp_rate_table[i]) + break; + } + if (i == ARRAY_SIZE(amdtp_rate_table)) return -EINVAL; err = snd_ff_stream_get_multiplier_mode(i, &mode); if (err < 0) return err; - /* Keep resources for in-stream. */ + // Keep resources for in-stream. ff->tx_resources.channels_mask = 0x00000000000000ffuLL; err = fw_iso_resources_allocate(&ff->tx_resources, amdtp_stream_get_max_payload(&ff->tx_stream), @@ -123,7 +162,7 @@ static int keep_resources(struct snd_ff *ff, unsigned int rate) if (err < 0) return err; - /* Keep resources for out-stream. */ + // Keep resources for out-stream. ff->rx_resources.channels_mask = 0x00000000000000ffuLL; err = fw_iso_resources_allocate(&ff->rx_resources, amdtp_stream_get_max_payload(&ff->rx_stream), @@ -136,60 +175,30 @@ static int keep_resources(struct snd_ff *ff, unsigned int rate) static int latter_begin_session(struct snd_ff *ff, unsigned int rate) { - static const struct { - unsigned int stf; - unsigned int code; - unsigned int flag; - } *entry, rate_table[] = { - { 32000, 0x00, 0x92, }, - { 44100, 0x02, 0x92, }, - { 48000, 0x04, 0x92, }, - { 64000, 0x08, 0x8e, }, - { 88200, 0x0a, 0x8e, }, - { 96000, 0x0c, 0x8e, }, - { 128000, 0x10, 0x8c, }, - { 176400, 0x12, 0x8c, }, - { 192000, 0x14, 0x8c, }, - }; + unsigned int generation = ff->rx_resources.generation; + unsigned int flag; u32 data; __le32 reg; - unsigned int count; - int i; int err; - for (i = 0; i < ARRAY_SIZE(rate_table); ++i) { - entry = rate_table + i; - if (entry->stf == rate) - break; - } - if (i == ARRAY_SIZE(rate_table)) + if (rate >= 32000 && rate <= 48000) + flag = 0x92; + else if (rate >= 64000 && rate <= 96000) + flag = 0x8e; + else if (rate >= 128000 && rate <= 192000) + flag = 0x8c; + else return -EINVAL; - reg = cpu_to_le32(entry->code); - err = snd_fw_transaction(ff->unit, TCODE_WRITE_QUADLET_REQUEST, - LATTER_STF, ®, sizeof(reg), 0); - if (err < 0) - return err; - - // Confirm to shift transmission clock. - count = 0; - while (count++ < 10) { - unsigned int curr_rate; - enum snd_ff_clock_src src; - - err = latter_get_clock(ff, &curr_rate, &src); + if (generation != fw_parent_device(ff->unit)->card->generation) { + err = fw_iso_resources_update(&ff->tx_resources); if (err < 0) return err; - if (curr_rate == rate) - break; + err = fw_iso_resources_update(&ff->rx_resources); + if (err < 0) + return err; } - if (count == 10) - return -ETIMEDOUT; - - err = keep_resources(ff, rate); - if (err < 0) - return err; data = (ff->tx_resources.channel << 8) | ff->rx_resources.channel; reg = cpu_to_le32(data); @@ -200,7 +209,7 @@ static int latter_begin_session(struct snd_ff *ff, unsigned int rate) // Always use the maximum number of data channels in data block of // packet. - reg = cpu_to_le32(entry->flag); + reg = cpu_to_le32(flag); return snd_fw_transaction(ff->unit, TCODE_WRITE_QUADLET_REQUEST, LATTER_ISOC_START, ®, sizeof(reg), 0); } @@ -424,6 +433,7 @@ const struct snd_ff_protocol snd_ff_protocol_latter = { .fill_midi_msg = latter_fill_midi_msg, .get_clock = latter_get_clock, .switch_fetching_mode = latter_switch_fetching_mode, + .allocate_resources = latter_allocate_resources, .begin_session = latter_begin_session, .finish_session = latter_finish_session, .dump_status = latter_dump_status, diff --git a/sound/firewire/fireface/ff-stream.c b/sound/firewire/fireface/ff-stream.c index 6dfd2efb6646..4208b8004d1a 100644 --- a/sound/firewire/fireface/ff-stream.c +++ b/sound/firewire/fireface/ff-stream.c @@ -30,14 +30,11 @@ int snd_ff_stream_get_multiplier_mode(enum cip_sfc sfc, return 0; } -static void release_resources(struct snd_ff *ff) -{ - fw_iso_resources_free(&ff->tx_resources); - fw_iso_resources_free(&ff->rx_resources); -} - static inline void finish_session(struct snd_ff *ff) { + amdtp_stream_stop(&ff->tx_stream); + amdtp_stream_stop(&ff->rx_stream); + ff->spec->protocol->finish_session(ff); ff->spec->protocol->switch_fetching_mode(ff, false); } @@ -103,37 +100,25 @@ void snd_ff_stream_destroy_duplex(struct snd_ff *ff) destroy_stream(ff, AMDTP_OUT_STREAM); } -int snd_ff_stream_start_duplex(struct snd_ff *ff, unsigned int rate) +int snd_ff_stream_reserve_duplex(struct snd_ff *ff, unsigned int rate) { unsigned int curr_rate; enum snd_ff_clock_src src; int err; - if (ff->substreams_counter == 0) - return 0; - err = ff->spec->protocol->get_clock(ff, &curr_rate, &src); if (err < 0) return err; - if (curr_rate != rate || - amdtp_streaming_error(&ff->tx_stream) || - amdtp_streaming_error(&ff->rx_stream)) { - finish_session(ff); - - amdtp_stream_stop(&ff->tx_stream); - amdtp_stream_stop(&ff->rx_stream); - - release_resources(ff); - } - /* - * Regardless of current source of clock signal, drivers transfer some - * packets. Then, the device transfers packets. - */ - if (!amdtp_stream_running(&ff->rx_stream)) { + if (ff->substreams_counter == 0 || curr_rate != rate) { enum snd_ff_stream_mode mode; int i; + finish_session(ff); + + fw_iso_resources_free(&ff->tx_resources); + fw_iso_resources_free(&ff->rx_resources); + for (i = 0; i < CIP_SFC_COUNT; ++i) { if (amdtp_rate_table[i] == rate) break; @@ -155,6 +140,30 @@ int snd_ff_stream_start_duplex(struct snd_ff *ff, unsigned int rate) if (err < 0) return err; + err = ff->spec->protocol->allocate_resources(ff, rate); + if (err < 0) + return err; + } + + return 0; +} + +int snd_ff_stream_start_duplex(struct snd_ff *ff, unsigned int rate) +{ + int err; + + if (ff->substreams_counter == 0) + return 0; + + if (amdtp_streaming_error(&ff->tx_stream) || + amdtp_streaming_error(&ff->rx_stream)) + finish_session(ff); + + /* + * Regardless of current source of clock signal, drivers transfer some + * packets. Then, the device transfers packets. + */ + if (!amdtp_stream_running(&ff->rx_stream)) { err = ff->spec->protocol->begin_session(ff, rate); if (err < 0) goto error; @@ -192,37 +201,29 @@ int snd_ff_stream_start_duplex(struct snd_ff *ff, unsigned int rate) return 0; error: - amdtp_stream_stop(&ff->tx_stream); - amdtp_stream_stop(&ff->rx_stream); - finish_session(ff); - release_resources(ff); return err; } void snd_ff_stream_stop_duplex(struct snd_ff *ff) { - if (ff->substreams_counter > 0) - return; + if (ff->substreams_counter == 0) { + finish_session(ff); - amdtp_stream_stop(&ff->tx_stream); - amdtp_stream_stop(&ff->rx_stream); - finish_session(ff); - release_resources(ff); + fw_iso_resources_free(&ff->tx_resources); + fw_iso_resources_free(&ff->rx_resources); + } } void snd_ff_stream_update_duplex(struct snd_ff *ff) { - /* The device discontinue to transfer packets. */ + // The device discontinue to transfer packets. amdtp_stream_pcm_abort(&ff->tx_stream); amdtp_stream_stop(&ff->tx_stream); amdtp_stream_pcm_abort(&ff->rx_stream); amdtp_stream_stop(&ff->rx_stream); - - fw_iso_resources_update(&ff->tx_resources); - fw_iso_resources_update(&ff->rx_resources); } void snd_ff_stream_lock_changed(struct snd_ff *ff) diff --git a/sound/firewire/fireface/ff.h b/sound/firewire/fireface/ff.h index 7fac241c2486..36dd0c75b9f7 100644 --- a/sound/firewire/fireface/ff.h +++ b/sound/firewire/fireface/ff.h @@ -112,6 +112,7 @@ struct snd_ff_protocol { int (*get_clock)(struct snd_ff *ff, unsigned int *rate, enum snd_ff_clock_src *src); int (*switch_fetching_mode)(struct snd_ff *ff, bool enable); + int (*allocate_resources)(struct snd_ff *ff, unsigned int rate); int (*begin_session)(struct snd_ff *ff, unsigned int rate); void (*finish_session)(struct snd_ff *ff); void (*dump_status)(struct snd_ff *ff, struct snd_info_buffer *buffer); @@ -136,6 +137,7 @@ int snd_ff_stream_get_multiplier_mode(enum cip_sfc sfc, enum snd_ff_stream_mode *mode); int snd_ff_stream_init_duplex(struct snd_ff *ff); void snd_ff_stream_destroy_duplex(struct snd_ff *ff); +int snd_ff_stream_reserve_duplex(struct snd_ff *ff, unsigned int rate); int snd_ff_stream_start_duplex(struct snd_ff *ff, unsigned int rate); void snd_ff_stream_stop_duplex(struct snd_ff *ff); void snd_ff_stream_update_duplex(struct snd_ff *ff); |