From: Takashi S. <o-t...@sa...> - 2014-01-29 13:45:23
|
This series of patch is to update patches which I've posted below. [RFC v2][PATCH 00/38] Enhancement for support of some firewire devices http://mailman.alsa-project.org/pipermail/alsa-devel/2013-December/070424.html [RFC][PATCH v2 00/13] speakers: Add support for capture/playback of PCM/MIDI http://mailman.alsa-project.org/pipermail/alsa-devel/2014-January/071071.html This series may be last RFC. Repository: https://github.com/takaswie/sound For backport to Linux kernel 3.11 or later: https://github.com/takaswie/snd-firewire-improve For maintainers: I have 4 items. 1.expected impacts to user-land Refer to my last message. 2. Address allocation for Fireworks drivers Related to this suggestion: http://mailman.alsa-project.org/pipermail/alsa-devel/2014-January/071120.html The alternative way is to implement: 1.'AV/C SIGNAL FORMAT' commanids defined in 'AV/C Digital Interface Command Set General Specification Version 4.2 (Sep 2004, 1394TA)' - 'snd-firewire-lib' has these commands 2.'AV/C DESCRIPTOR' mechanism defined in 'Enhancements to the AV/C General Specification 3.0 Version 1.1 (Oct 2000, 1394TA)' 3.'AV/C SIGNAL SOURCE' commands defined in 'AV/C Music Subunit 1.0' - I don't have this specification. They cost higher than actual effects. Especially, 2 is a bit complicated and need much codes. Actually FFADO has all of them. And snd-fireworks gives hwdep interface for application to command/response. There is currently several ways in user-land so I think it good to keep the address non-relocatable yet. 3.Support for devices with fatal quirk M-Audio 'Firewire 1814' and 'ProjectMix I/O' has BeBoB firmware which is heavily customized. See last patch. They have a time not to respond transaction. As long as I experienced, the time may be 300 msec per 1 sec. As a result, snd-bebob often fail to handle them. Especially, they can ignore transactions for resource management of connection/channel/bandwidth. This is a fatal for host system. But there is no user-land driver for these devices and I received some requests from FFADO users to support them. I hope to get your opinion about this support. 4.Rest of my work I've finished almost work for them. If I receive no big problems from developers and testers, I have a plan to post merge-request. Updates since previous patches: snd-firewire-lib: - remove wrong line to regain 'stream_running' member to 'struct fwspk' - allow drivers to indicate timeout value - remove 'amdtp_stream_midi_running' snd-bebob: - add reference counter for MIDI substreams - improve 'mutex_lock()/mutex_unlock()' for streaming - remove 'device' member fron 'struct snd_bebob' - update device entry snd-fireworks: - add reference counter for MIDI substreams - improve 'mutex_lock()/mutex_unlock()' for streaming - remove 'device' member from 'struct snd_fireworks' snd-firewire-speakers: - rename to snd-oxfw and obsolete snd-oxfw: - add reference counter for MIDI substreams - improve 'mutex_lock()/mutex_unlock()' for streaming - fix lock mechanism of streaming by hwdep - fix wrong formation for Lacie FWSpeaker and Griffin Firewave - remove 'fw_unit_get()/fw_unit_put()' because helper function do this Confirm to work with: snd-fireworks: - Echo Audio AudioFire4 - Echo Audio AudioFire8 (by tester) - Echo Audio AudioFirePre8 - Echo Audio AudioFire12 snd-bebob: - Edirol FA-66 (by tester) - M-Audio Ozonic - M-Audio Firewire Solo - M-Audio Firewire Audiophile - M-Audio Firewire 410 - M-Audio Firewire 1814 (by tester) - PreSonus FIREBOX - PreSonus FP10 (former known as FIREPOD, by tester) - Yamaha GO44 - Yamaha GO46 snd-oxfw: - Behringer F-Control Audio 202 Takashi Sakamoto (52): firewire-lib: Rename functions, structure, member for AMDTP firewire-lib: Add macros instead of fixed value for AMDTP firewire-lib: Add 'direction' member to 'amdtp_stream' structure firewire-lib: Split some codes into functions to reuse for both streams firewire-lib: Add support for AMDTP in-stream and PCM capture firewire-lib: Add support for MIDI capture/playback firewire-lib: Give syt value as parameter to handle_out_packet() firewire-lib: Add support for duplex streams synchronization in blocking mode firewire-lib: Add sort function for transmitted packet firewire-lib: Add transfer delay to synchronized duplex streams firewire-lib: Add support for channel mapping firewire-lib: Rename macros, variables and functions for CMP firewire-lib: Add 'direction' member to 'cmp_connection' structure firewire-lib: Add handling output connection by CMP firewire-lib: Add a new function to check others' connection firewire-lib: Add some AV/C general commands firewire-lib: Add quirks for Fireworks fireworks: Add skelton for Fireworks based devices fireworks: Add transaction and some commands fireworks: Add connection and stream management fireworks: Add proc interface for debugging purpose fireworks: Add MIDI interface fireworks: Add PCM interface fireworks: Add hwdep interface fireworks: Add command/response functionality into hwdep interface bebob: Add skelton for BeBoB based devices bebob: Add commands and connections/streams management bebob: Add proc interface for debugging purpose bebob: Add MIDI interface bebob: Add PCM interface bebob: Add hwdep interface bebob: Prepare for device specific operations bebob: Add support for Terratec PHASE, EWS series and Aureon bebob: Add support for Yamaha GO series bebob: Add support for Focusrite Saffire/SaffirePro series bebob: Add support for M-Audio usual Firewire series bebob: Send a cue to load firmware for M-Audio Firewire series speakers: Rename to oxfw oxfw: Move to its own directory oxfw: Split stream functionality to a new file and add a header file oxfw: Split PCM functionality to a new file oxfw: Split control functionality to a new file oxfw: Change the way to name card oxfw: Change the way to make PCM rules/constraints oxfw: Add proc interface for debugging purpose oxfw: Change the way to start stream oxfw: Add some AV/C commands to get stream formation and supported sample rate oxfw: Add support for Behringer/Mackie devices oxfw: Add support AMDTP in-stream and PCM capture oxfw: Add support for capture/playback MIDI messages oxfw: Add hwdep interface bebob: Add support for M-Audio special Firewire series include/uapi/sound/asound.h | 5 +- include/uapi/sound/firewire.h | 23 +- sound/firewire/Kconfig | 76 +- sound/firewire/Makefile | 5 +- sound/firewire/amdtp.c | 905 ++++++++++++++++------ sound/firewire/amdtp.h | 161 +++- sound/firewire/bebob/Makefile | 4 + sound/firewire/bebob/bebob.c | 453 +++++++++++ sound/firewire/bebob/bebob.h | 262 +++++++ sound/firewire/bebob/bebob_command.c | 354 +++++++++ sound/firewire/bebob/bebob_focusrite.c | 289 +++++++ sound/firewire/bebob/bebob_hwdep.c | 197 +++++ sound/firewire/bebob/bebob_maudio.c | 922 +++++++++++++++++++++++ sound/firewire/bebob/bebob_midi.c | 178 +++++ sound/firewire/bebob/bebob_pcm.c | 419 ++++++++++ sound/firewire/bebob/bebob_proc.c | 185 +++++ sound/firewire/bebob/bebob_stream.c | 873 +++++++++++++++++++++ sound/firewire/bebob/bebob_terratec.c | 68 ++ sound/firewire/bebob/bebob_yamaha.c | 50 ++ sound/firewire/cmp.c | 217 ++++-- sound/firewire/cmp.h | 14 +- sound/firewire/dice.c | 46 +- sound/firewire/fcp.c | 151 ++++ sound/firewire/fcp.h | 21 + sound/firewire/fireworks/Makefile | 4 + sound/firewire/fireworks/fireworks.c | 330 ++++++++ sound/firewire/fireworks/fireworks.h | 223 ++++++ sound/firewire/fireworks/fireworks_command.c | 397 ++++++++++ sound/firewire/fireworks/fireworks_hwdep.c | 299 ++++++++ sound/firewire/fireworks/fireworks_midi.c | 186 +++++ sound/firewire/fireworks/fireworks_pcm.c | 443 +++++++++++ sound/firewire/fireworks/fireworks_proc.c | 206 +++++ sound/firewire/fireworks/fireworks_stream.c | 356 +++++++++ sound/firewire/fireworks/fireworks_transaction.c | 327 ++++++++ sound/firewire/oxfw/Makefile | 3 + sound/firewire/oxfw/oxfw.c | 264 +++++++ sound/firewire/oxfw/oxfw.h | 146 ++++ sound/firewire/oxfw/oxfw_command.c | 162 ++++ sound/firewire/oxfw/oxfw_control.c | 283 +++++++ sound/firewire/oxfw/oxfw_hwdep.c | 195 +++++ sound/firewire/oxfw/oxfw_midi.c | 176 +++++ sound/firewire/oxfw/oxfw_pcm.c | 399 ++++++++++ sound/firewire/oxfw/oxfw_proc.c | 60 ++ sound/firewire/oxfw/oxfw_stream.c | 582 ++++++++++++++ sound/firewire/speakers.c | 830 -------------------- 45 files changed, 10576 insertions(+), 1173 deletions(-) create mode 100644 sound/firewire/bebob/Makefile create mode 100644 sound/firewire/bebob/bebob.c create mode 100644 sound/firewire/bebob/bebob.h create mode 100644 sound/firewire/bebob/bebob_command.c create mode 100644 sound/firewire/bebob/bebob_focusrite.c create mode 100644 sound/firewire/bebob/bebob_hwdep.c create mode 100644 sound/firewire/bebob/bebob_maudio.c create mode 100644 sound/firewire/bebob/bebob_midi.c create mode 100644 sound/firewire/bebob/bebob_pcm.c create mode 100644 sound/firewire/bebob/bebob_proc.c create mode 100644 sound/firewire/bebob/bebob_stream.c create mode 100644 sound/firewire/bebob/bebob_terratec.c create mode 100644 sound/firewire/bebob/bebob_yamaha.c create mode 100644 sound/firewire/fireworks/Makefile create mode 100644 sound/firewire/fireworks/fireworks.c create mode 100644 sound/firewire/fireworks/fireworks.h create mode 100644 sound/firewire/fireworks/fireworks_command.c create mode 100644 sound/firewire/fireworks/fireworks_hwdep.c create mode 100644 sound/firewire/fireworks/fireworks_midi.c create mode 100644 sound/firewire/fireworks/fireworks_pcm.c create mode 100644 sound/firewire/fireworks/fireworks_proc.c create mode 100644 sound/firewire/fireworks/fireworks_stream.c create mode 100644 sound/firewire/fireworks/fireworks_transaction.c create mode 100644 sound/firewire/oxfw/Makefile create mode 100644 sound/firewire/oxfw/oxfw.c create mode 100644 sound/firewire/oxfw/oxfw.h create mode 100644 sound/firewire/oxfw/oxfw_command.c create mode 100644 sound/firewire/oxfw/oxfw_control.c create mode 100644 sound/firewire/oxfw/oxfw_hwdep.c create mode 100644 sound/firewire/oxfw/oxfw_midi.c create mode 100644 sound/firewire/oxfw/oxfw_pcm.c create mode 100644 sound/firewire/oxfw/oxfw_proc.c create mode 100644 sound/firewire/oxfw/oxfw_stream.c delete mode 100644 sound/firewire/speakers.c -- 1.8.3.2 Regards Takashi Sakamoto o-t...@sa... |
From: Takashi S. <o-t...@sa...> - 2014-01-29 13:45:25
|
This patch adds some macros instead of fixed value for AMDTP in IEC 61883-6. These macros will also be used by followed patches. Signed-off-by: Takashi Sakamoto <o-t...@sa...> --- sound/firewire/amdtp.c | 30 +++++++++++++++++++++++------- 1 file changed, 23 insertions(+), 7 deletions(-) diff --git a/sound/firewire/amdtp.c b/sound/firewire/amdtp.c index 16fe858..38013f9 100644 --- a/sound/firewire/amdtp.c +++ b/sound/firewire/amdtp.c @@ -20,12 +20,28 @@ #define TRANSFER_DELAY_TICKS 0x2e00 /* 479.17 µs */ +/* isochronous header parameters */ +#define ISO_DATA_LENGTH_SHIFT 16 #define TAG_CIP 1 +/* common isochronous packet header parameters */ #define CIP_EOH (1u << 31) +#define CIP_EOH_MASK 0x80000000 #define CIP_FMT_AM (0x10 << 24) -#define AMDTP_FDF_AM824 (0 << 19) -#define AMDTP_FDF_SFC_SHIFT 16 +#define CIP_FMT_MASK 0x3f000000 +#define CIP_SYT_MASK 0x0000ffff +#define CIP_SYT_NO_INFO 0xffff +#define CIP_FDF_MASK 0x00ff0000 +#define CIP_FDF_SFC_SHIFT 16 + +/* + * Audio and Music transfer protocol specific parameters + * only "Clock-based rate control mode" is supported + */ +#define AMDTP_FDF_AM824 (0 << (CIP_FDF_SFC_SHIFT + 3)) +#define AMDTP_DBS_MASK 0x00ff0000 +#define AMDTP_DBS_SHIFT 16 +#define AMDTP_DBC_MASK 0x000000ff /* TODO: make these configurable */ #define INTERRUPT_INTERVAL 16 @@ -280,9 +296,9 @@ static unsigned int calculate_syt(struct amdtp_stream *s, syt = (cycle + syt_offset / TICKS_PER_CYCLE) << 12; syt += syt_offset % TICKS_PER_CYCLE; - return syt & 0xffff; + return syt & CIP_SYT_MASK; } else { - return 0xffff; /* no info */ + return CIP_SYT_NO_INFO; } } @@ -438,17 +454,17 @@ static void queue_out_packet(struct amdtp_stream *s, unsigned int cycle) syt = calculate_syt(s, cycle); if (!(s->flags & CIP_BLOCKING)) data_blocks = calculate_data_blocks(s); - else if (syt != 0xffff) + else if (syt != CIP_SYT_NO_INFO) data_blocks = s->syt_interval; else data_blocks = 0; buffer = s->buffer.packets[index].buffer; buffer[0] = cpu_to_be32(ACCESS_ONCE(s->source_node_id_field) | - (s->data_block_quadlets << 16) | + (s->data_block_quadlets << AMDTP_DBS_SHIFT) | s->data_block_counter); buffer[1] = cpu_to_be32(CIP_EOH | CIP_FMT_AM | AMDTP_FDF_AM824 | - (s->sfc << AMDTP_FDF_SFC_SHIFT) | syt); + (s->sfc << CIP_FDF_SFC_SHIFT) | syt); buffer += 2; pcm = ACCESS_ONCE(s->pcm); -- 1.8.3.2 |
From: Takashi S. <o-t...@sa...> - 2014-01-29 13:45:30
|
For capturing/playbacking MIDI messages, this commit adds one MIDI conformant data channel. This channel supports 8 MPX-MIDI data streams then maximum number of supported MIDI ports is limited by 8. And this commit allows to set PCM format even if AMDTP stream already starts. I suppose the case that PCM stream is going to be joined into AMDTP streams after AMDTP stream is already started by MIDI. Each driver must count how many MIDI substreams use AMDTP stream to stop AMDTP stream. IEC 61883-6 refers to AMEI/MMA RP-027 for implementation of MIDI conformant data. In the specification, 'MIDI1.0-2x/3x-SPEED' mode are described with 'negotiation procedure' and 'encapsulation details'. But there is no specifications to define them. This module implement 'MIDI1.0-1x-SPEED' mode. Signed-off-by: Takashi Sakamoto <o-t...@sa...> --- sound/firewire/amdtp.c | 63 ++++++++++++++++++++++++++++++++++++++++++-------- sound/firewire/amdtp.h | 43 ++++++++++++++++++++++++++++++++++ 2 files changed, 96 insertions(+), 10 deletions(-) diff --git a/sound/firewire/amdtp.c b/sound/firewire/amdtp.c index 4ebfd67..ad3521f 100644 --- a/sound/firewire/amdtp.c +++ b/sound/firewire/amdtp.c @@ -12,6 +12,7 @@ #include <linux/module.h> #include <linux/slab.h> #include <sound/pcm.h> +#include <sound/rawmidi.h> #include "amdtp.h" #define TICKS_PER_CYCLE 3072 @@ -123,9 +124,12 @@ void amdtp_stream_set_parameters(struct amdtp_stream *s, [CIP_SFC_176400] = 176400, [CIP_SFC_192000] = 192000, }; - unsigned int sfc; + unsigned int sfc, midi_channels; - if (WARN_ON(amdtp_stream_running(s))) + midi_channels = DIV_ROUND_UP(midi_ports, 8); + + if (WARN_ON(amdtp_stream_running(s)) || + WARN_ON(midi_channels > AMDTP_MAX_CHANNELS_FOR_MIDI)) return; for (sfc = 0; sfc < CIP_SFC_COUNT; ++sfc) @@ -142,7 +146,7 @@ sfc_found: pcm_channels *= 2; } s->sfc = sfc; - s->data_block_quadlets = pcm_channels + DIV_ROUND_UP(midi_ports, 8); + s->data_block_quadlets = pcm_channels + midi_channels; s->pcm_channels = pcm_channels; s->midi_ports = midi_ports; @@ -200,7 +204,7 @@ static void amdtp_read_s32_dualwire(struct amdtp_stream *s, void amdtp_stream_set_pcm_format(struct amdtp_stream *s, snd_pcm_format_t format) { - if (WARN_ON(amdtp_stream_running(s))) + if (WARN_ON(amdtp_stream_pcm_running(s))) return; switch (format) { @@ -505,11 +509,46 @@ static void amdtp_fill_pcm_silence(struct amdtp_stream *s, static void amdtp_fill_midi(struct amdtp_stream *s, __be32 *buffer, unsigned int frames) { - unsigned int i; + unsigned int f, port; + u8 *b; + + for (f = 0; f < frames; f++) { + buffer[s->pcm_channels + 1] = 0x00; + b = (u8 *)&buffer[s->pcm_channels + 1]; + + port = (s->data_block_counter + f) % 8; + if ((s->midi[port] == NULL) || + (snd_rawmidi_transmit(s->midi[port], b + 1, 1) <= 0)) { + b[0] = 0x80; + b[1] = 0x00; /* confirm to be zero */ + } else { + b[0] = 0x81; + } + buffer += s->data_block_quadlets; + } +} + +static void amdtp_pull_midi(struct amdtp_stream *s, + __be32 *buffer, unsigned int frames) +{ + unsigned int f, port; + int len; + u8 *b; + + for (f = 0; f < frames; f++) { + port = (s->data_block_counter + f) % 8; + b = (u8 *)&buffer[s->pcm_channels + 1]; + + len = b[0] - 0x80; + if (len < 1 || 3 < len) + continue; - for (i = 0; i < frames; ++i) - buffer[s->pcm_channels + i * s->data_block_quadlets] = - cpu_to_be32(0x80000000); + if (s->midi[port] == NULL) + continue; + + snd_rawmidi_receive(s->midi[port], b + 1, len); + buffer += s->data_block_quadlets; + } } static void update_pcm_pointers(struct amdtp_stream *s, @@ -683,10 +722,14 @@ static void handle_in_packet(struct amdtp_stream *s, buffer += 2; pcm = ACCESS_ONCE(s->pcm); - if (pcm) { + if (pcm) s->transfer_samples(s, pcm, buffer, data_blocks); + + if (s->midi_ports) + amdtp_pull_midi(s, buffer, data_blocks); + + if (pcm) update_pcm_pointers(s, pcm, data_blocks); - } } static void out_stream_callback(struct fw_iso_context *context, u32 cycle, diff --git a/sound/firewire/amdtp.h b/sound/firewire/amdtp.h index 019134e..96f25f9 100644 --- a/sound/firewire/amdtp.h +++ b/sound/firewire/amdtp.h @@ -44,9 +44,21 @@ enum cip_sfc { #define AMDTP_OUT_PCM_FORMAT_BITS (SNDRV_PCM_FMTBIT_S16 | \ SNDRV_PCM_FMTBIT_S32) + +/* + * AMDTP packet can include channels for MIDI conformant data. + * Each MIDI conformant data channel includes 8 MPX-MIDI data stream. + * Each MPX-MIDI data stream includes one data stream from/to MIDI ports. + * + * This module supports maximum 1 MIDI conformant data channels. + * Then this AMDTP packets can transfer maximum 8 MIDI data streams. + */ +#define AMDTP_MAX_CHANNELS_FOR_MIDI 1 + struct fw_unit; struct fw_iso_context; struct snd_pcm_substream; +struct snd_rawmidi_substream; enum amdtp_stream_direction { AMDTP_OUT_STREAM = 0, @@ -88,6 +100,8 @@ struct amdtp_stream { unsigned int pcm_buffer_pointer; unsigned int pcm_period_pointer; bool pointer_flush; + + struct snd_rawmidi_substream *midi[AMDTP_MAX_CHANNELS_FOR_MIDI * 8]; }; int amdtp_stream_init(struct amdtp_stream *s, struct fw_unit *unit, @@ -137,6 +151,17 @@ static inline bool amdtp_streaming_error(struct amdtp_stream *s) } /** + * amdtp_stream_pcm_running - check PCM stream is running or not + * @s: the AMDTP stream + * + * If this function returns true, PCM stream in the stream is running. + */ +static inline bool amdtp_stream_pcm_running(struct amdtp_stream *s) +{ + return !IS_ERR_OR_NULL(s->pcm); +} + +/** * amdtp_stream_pcm_trigger - start/stop playback from a PCM device * @s: the AMDTP stream * @pcm: the PCM device to be started, or %NULL to stop the current device @@ -151,6 +176,24 @@ static inline void amdtp_stream_pcm_trigger(struct amdtp_stream *s, ACCESS_ONCE(s->pcm) = pcm; } +/** + * amdtp_stream_midi_trigger - start/stop playback/capture with a MIDI device + * @s: the AMDTP stream + * @port: index of MIDI port + * @midi: the MIDI device to be started, or %NULL to stop the current device + * + * Call this function on a running isochronous stream to enable the actual + * transmission of MIDI data. This function should be called from the MIDI + * device's .trigger callback. + */ +static inline void amdtp_stream_midi_trigger(struct amdtp_stream *s, + unsigned int port, + struct snd_rawmidi_substream *midi) +{ + if (port < s->midi_ports) + ACCESS_ONCE(s->midi[port]) = midi; +} + static inline bool cip_sfc_is_base_44100(enum cip_sfc sfc) { return sfc & 1; -- 1.8.3.2 |
From: Takashi S. <o-t...@sa...> - 2014-01-29 13:45:25
|
This patch adds 'direction' member to amdtp_stream structure to indicate its direction. This patch also adds 'direction' argument to amdtp_stream_init() function to determine its direction. The amdtp_stream_init() function is exported and used by firewire-speakers and dice so this patch also affects them. This patch just add them. Actual implementation will be done by followed patches. Signed-off-by: Takashi Sakamoto <o-t...@sa...> --- sound/firewire/amdtp.c | 4 +++- sound/firewire/amdtp.h | 7 +++++++ sound/firewire/dice.c | 2 +- sound/firewire/speakers.c | 3 ++- 4 files changed, 13 insertions(+), 3 deletions(-) diff --git a/sound/firewire/amdtp.c b/sound/firewire/amdtp.c index 38013f9..95c5a15 100644 --- a/sound/firewire/amdtp.c +++ b/sound/firewire/amdtp.c @@ -53,12 +53,14 @@ static void pcm_period_tasklet(unsigned long data); * amdtp_stream_init - initialize an AMDTP stream structure * @s: the AMDTP stream to initialize * @unit: the target of the stream + * @dir: the direction of stream * @flags: the packet transmission method to use */ int amdtp_stream_init(struct amdtp_stream *s, struct fw_unit *unit, - enum cip_flags flags) + enum amdtp_stream_direction dir, enum cip_flags flags) { s->unit = fw_unit_get(unit); + s->direction = dir; s->flags = flags; s->context = ERR_PTR(-1); mutex_init(&s->mutex); diff --git a/sound/firewire/amdtp.h b/sound/firewire/amdtp.h index 60028c7..019134e 100644 --- a/sound/firewire/amdtp.h +++ b/sound/firewire/amdtp.h @@ -48,9 +48,15 @@ struct fw_unit; struct fw_iso_context; struct snd_pcm_substream; +enum amdtp_stream_direction { + AMDTP_OUT_STREAM = 0, + AMDTP_IN_STREAM +}; + struct amdtp_stream { struct fw_unit *unit; enum cip_flags flags; + enum amdtp_stream_direction direction; struct fw_iso_context *context; struct mutex mutex; @@ -85,6 +91,7 @@ struct amdtp_stream { }; int amdtp_stream_init(struct amdtp_stream *s, struct fw_unit *unit, + enum amdtp_stream_direction dir, enum cip_flags flags); void amdtp_stream_destroy(struct amdtp_stream *s); diff --git a/sound/firewire/dice.c b/sound/firewire/dice.c index 88814cc..b4d50b0 100644 --- a/sound/firewire/dice.c +++ b/sound/firewire/dice.c @@ -1360,7 +1360,7 @@ static int dice_probe(struct fw_unit *unit, const struct ieee1394_device_id *id) goto err_owner; dice->resources.channels_mask = 0x00000000ffffffffuLL; - err = amdtp_stream_init(&dice->stream, unit, + err = amdtp_stream_init(&dice->stream, unit, AMDTP_OUT_STREAM, CIP_BLOCKING | CIP_HI_DUALWIRE); if (err < 0) goto err_resources; diff --git a/sound/firewire/speakers.c b/sound/firewire/speakers.c index 554818e..f5d0bf3 100644 --- a/sound/firewire/speakers.c +++ b/sound/firewire/speakers.c @@ -683,7 +683,8 @@ static int fwspk_probe(struct fw_unit *unit, if (err < 0) goto err_unit; - err = amdtp_stream_init(&fwspk->stream, unit, CIP_NONBLOCKING); + err = amdtp_stream_init(&fwspk->stream, unit, AMDTP_OUT_STREAM, + CIP_NONBLOCKING); if (err < 0) goto err_connection; -- 1.8.3.2 |
From: Takashi S. <o-t...@sa...> - 2014-01-29 13:45:27
|
Some codes can be reused to handle in stream. This commit adds new functions. This commit also renames some functions to keep naming consistency. Signed-off-by: Takashi Sakamoto <o-t...@sa...> --- sound/firewire/amdtp.c | 156 ++++++++++++++++++++++++++----------------------- 1 file changed, 82 insertions(+), 74 deletions(-) diff --git a/sound/firewire/amdtp.c b/sound/firewire/amdtp.c index 95c5a15..ca79cb4 100644 --- a/sound/firewire/amdtp.c +++ b/sound/firewire/amdtp.c @@ -47,6 +47,8 @@ #define INTERRUPT_INTERVAL 16 #define QUEUE_LENGTH 48 +#define OUT_PACKET_HEADER_SIZE 0 + static void pcm_period_tasklet(unsigned long data); /** @@ -440,13 +442,74 @@ static void amdtp_fill_midi(struct amdtp_stream *s, cpu_to_be32(0x80000000); } -static void queue_out_packet(struct amdtp_stream *s, unsigned int cycle) +static void update_pcm_pointers(struct amdtp_stream *s, + struct snd_pcm_substream *pcm, + unsigned int frames) +{ unsigned int ptr; + + if (s->dual_wire) + frames *= 2; + + ptr = s->pcm_buffer_pointer + frames; + if (ptr >= pcm->runtime->buffer_size) + ptr -= pcm->runtime->buffer_size; + ACCESS_ONCE(s->pcm_buffer_pointer) = ptr; + + s->pcm_period_pointer += frames; + if (s->pcm_period_pointer >= pcm->runtime->period_size) { + s->pcm_period_pointer -= pcm->runtime->period_size; + s->pointer_flush = false; + tasklet_hi_schedule(&s->period_tasklet); + } +} + +static void pcm_period_tasklet(unsigned long data) +{ + struct amdtp_stream *s = (void *)data; + struct snd_pcm_substream *pcm = ACCESS_ONCE(s->pcm); + + if (pcm) + snd_pcm_period_elapsed(pcm); +} + +static int queue_packet(struct amdtp_stream *s, + unsigned int header_length, + unsigned int payload_length, bool skip) +{ + struct fw_iso_packet p = {0}; + int err; + + p.interrupt = IS_ALIGNED(s->packet_index + 1, INTERRUPT_INTERVAL); + p.tag = TAG_CIP; + p.header_length = header_length; + p.payload_length = (!skip) ? payload_length : 0; + p.skip = skip; + err = fw_iso_context_queue(s->context, &p, &s->buffer.iso_buffer, + s->buffer.packets[s->packet_index].offset); + if (err < 0) { + dev_err(&s->unit->device, "queueing error: %d\n", err); + s->packet_index = -1; + goto end; + } + + if (++s->packet_index >= QUEUE_LENGTH) + s->packet_index = 0; +end: + return err; +} + +static inline int queue_out_packet(struct amdtp_stream *s, + unsigned int payload_length, bool skip) +{ + return queue_packet(s, OUT_PACKET_HEADER_SIZE, + payload_length, skip); +} + +static void handle_out_packet(struct amdtp_stream *s, unsigned int cycle) { __be32 *buffer; - unsigned int index, data_blocks, syt, ptr; + unsigned int index, data_blocks, syt, payload_length; struct snd_pcm_substream *pcm; - struct fw_iso_packet packet; - int err; if (s->packet_index < 0) return; @@ -479,55 +542,19 @@ static void queue_out_packet(struct amdtp_stream *s, unsigned int cycle) s->data_block_counter = (s->data_block_counter + data_blocks) & 0xff; - packet.payload_length = 8 + data_blocks * 4 * s->data_block_quadlets; - packet.interrupt = IS_ALIGNED(index + 1, INTERRUPT_INTERVAL); - packet.skip = 0; - packet.tag = TAG_CIP; - packet.sy = 0; - packet.header_length = 0; - - err = fw_iso_context_queue(s->context, &packet, &s->buffer.iso_buffer, - s->buffer.packets[index].offset); - if (err < 0) { - dev_err(&s->unit->device, "queueing error: %d\n", err); - s->packet_index = -1; + payload_length = 8 + data_blocks * 4 * s->data_block_quadlets; + if (queue_out_packet(s, payload_length, false) < 0) { amdtp_stream_pcm_abort(s); return; } - if (++index >= QUEUE_LENGTH) - index = 0; - s->packet_index = index; - - if (pcm) { - if (s->dual_wire) - data_blocks *= 2; - - ptr = s->pcm_buffer_pointer + data_blocks; - if (ptr >= pcm->runtime->buffer_size) - ptr -= pcm->runtime->buffer_size; - ACCESS_ONCE(s->pcm_buffer_pointer) = ptr; - - s->pcm_period_pointer += data_blocks; - if (s->pcm_period_pointer >= pcm->runtime->period_size) { - s->pcm_period_pointer -= pcm->runtime->period_size; - s->pointer_flush = false; - tasklet_hi_schedule(&s->period_tasklet); - } - } -} - -static void pcm_period_tasklet(unsigned long data) -{ - struct amdtp_stream *s = (void *)data; - struct snd_pcm_substream *pcm = ACCESS_ONCE(s->pcm); - if (pcm) - snd_pcm_period_elapsed(pcm); + update_pcm_pointers(s, pcm, data_blocks); } -static void out_packet_callback(struct fw_iso_context *context, u32 cycle, - size_t header_length, void *header, void *private_data) +static void out_stream_callback(struct fw_iso_context *context, u32 cycle, + size_t header_length, void *header, + void *private_data) { struct amdtp_stream *s = private_data; unsigned int i, packets = header_length / 4; @@ -540,31 +567,10 @@ static void out_packet_callback(struct fw_iso_context *context, u32 cycle, cycle += QUEUE_LENGTH - packets; for (i = 0; i < packets; ++i) - queue_out_packet(s, ++cycle); + handle_out_packet(s, ++cycle); fw_iso_context_queue_flush(s->context); } -static int queue_initial_skip_packets(struct amdtp_stream *s) -{ - struct fw_iso_packet skip_packet = { - .skip = 1, - }; - unsigned int i; - int err; - - for (i = 0; i < QUEUE_LENGTH; ++i) { - skip_packet.interrupt = IS_ALIGNED(s->packet_index + 1, - INTERRUPT_INTERVAL); - err = fw_iso_context_queue(s->context, &skip_packet, NULL, 0); - if (err < 0) - return err; - if (++s->packet_index >= QUEUE_LENGTH) - s->packet_index = 0; - } - - return 0; -} - /** * amdtp_stream_start - start transferring packets * @s: the AMDTP stream to start @@ -594,7 +600,7 @@ int amdtp_stream_start(struct amdtp_stream *s, int channel, int speed) mutex_lock(&s->mutex); if (WARN_ON(amdtp_stream_running(s) || - (!s->pcm_channels && !s->midi_ports))) { + (s->data_block_quadlets < 1))) { err = -EBADFD; goto err_unlock; } @@ -612,7 +618,7 @@ int amdtp_stream_start(struct amdtp_stream *s, int channel, int speed) s->context = fw_iso_context_create(fw_parent_device(s->unit)->card, FW_ISO_CONTEXT_TRANSMIT, channel, speed, 0, - out_packet_callback, s); + out_stream_callback, s); if (IS_ERR(s->context)) { err = PTR_ERR(s->context); if (err == -EBUSY) @@ -624,11 +630,13 @@ int amdtp_stream_start(struct amdtp_stream *s, int channel, int speed) amdtp_stream_update(s); s->packet_index = 0; - s->data_block_counter = 0; - err = queue_initial_skip_packets(s); - if (err < 0) - goto err_context; + do { + err = queue_out_packet(s, 0, true); + if (err < 0) + goto err_context; + } while (s->packet_index > 0); + s->data_block_counter = 0; err = fw_iso_context_start(s->context, -1, 0, 0); if (err < 0) goto err_context; -- 1.8.3.2 |
From: Takashi S. <o-t...@sa...> - 2014-01-29 13:45:27
|
This patch renames some functions, structure and member to reuse them in both AMDTP in/out stream. Signed-off-by: Takashi Sakamoto <o-t...@sa...> --- sound/firewire/amdtp.c | 154 +++++++++++++++++++++++----------------------- sound/firewire/amdtp.h | 64 ++++++++++--------- sound/firewire/dice.c | 46 +++++++------- sound/firewire/speakers.c | 46 +++++++------- 4 files changed, 158 insertions(+), 152 deletions(-) diff --git a/sound/firewire/amdtp.c b/sound/firewire/amdtp.c index 9048777..16fe858 100644 --- a/sound/firewire/amdtp.c +++ b/sound/firewire/amdtp.c @@ -34,13 +34,13 @@ static void pcm_period_tasklet(unsigned long data); /** - * amdtp_out_stream_init - initialize an AMDTP output stream structure - * @s: the AMDTP output stream to initialize + * amdtp_stream_init - initialize an AMDTP stream structure + * @s: the AMDTP stream to initialize * @unit: the target of the stream * @flags: the packet transmission method to use */ -int amdtp_out_stream_init(struct amdtp_out_stream *s, struct fw_unit *unit, - enum cip_out_flags flags) +int amdtp_stream_init(struct amdtp_stream *s, struct fw_unit *unit, + enum cip_flags flags) { s->unit = fw_unit_get(unit); s->flags = flags; @@ -51,19 +51,19 @@ int amdtp_out_stream_init(struct amdtp_out_stream *s, struct fw_unit *unit, return 0; } -EXPORT_SYMBOL(amdtp_out_stream_init); +EXPORT_SYMBOL(amdtp_stream_init); /** - * amdtp_out_stream_destroy - free stream resources - * @s: the AMDTP output stream to destroy + * amdtp_stream_destroy - free stream resources + * @s: the AMDTP stream to destroy */ -void amdtp_out_stream_destroy(struct amdtp_out_stream *s) +void amdtp_stream_destroy(struct amdtp_stream *s) { - WARN_ON(amdtp_out_stream_running(s)); + WARN_ON(amdtp_stream_running(s)); mutex_destroy(&s->mutex); fw_unit_put(s->unit); } -EXPORT_SYMBOL(amdtp_out_stream_destroy); +EXPORT_SYMBOL(amdtp_stream_destroy); const unsigned int amdtp_syt_intervals[CIP_SFC_COUNT] = { [CIP_SFC_32000] = 8, @@ -77,8 +77,8 @@ const unsigned int amdtp_syt_intervals[CIP_SFC_COUNT] = { EXPORT_SYMBOL(amdtp_syt_intervals); /** - * amdtp_out_stream_set_parameters - set stream parameters - * @s: the AMDTP output stream to configure + * amdtp_stream_set_parameters - set stream parameters + * @s: the AMDTP stream to configure * @rate: the sample rate * @pcm_channels: the number of PCM samples in each data block, to be encoded * as AM824 multi-bit linear audio @@ -87,10 +87,10 @@ EXPORT_SYMBOL(amdtp_syt_intervals); * The parameters must be set before the stream is started, and must not be * changed while the stream is running. */ -void amdtp_out_stream_set_parameters(struct amdtp_out_stream *s, - unsigned int rate, - unsigned int pcm_channels, - unsigned int midi_ports) +void amdtp_stream_set_parameters(struct amdtp_stream *s, + unsigned int rate, + unsigned int pcm_channels, + unsigned int midi_ports) { static const unsigned int rates[] = { [CIP_SFC_32000] = 32000, @@ -103,7 +103,7 @@ void amdtp_out_stream_set_parameters(struct amdtp_out_stream *s, }; unsigned int sfc; - if (WARN_ON(amdtp_out_stream_running(s))) + if (WARN_ON(amdtp_stream_running(s))) return; for (sfc = 0; sfc < CIP_SFC_COUNT; ++sfc) @@ -132,47 +132,47 @@ sfc_found: /* additional buffering needed to adjust for no-data packets */ s->transfer_delay += TICKS_PER_SECOND * s->syt_interval / rate; } -EXPORT_SYMBOL(amdtp_out_stream_set_parameters); +EXPORT_SYMBOL(amdtp_stream_set_parameters); /** - * amdtp_out_stream_get_max_payload - get the stream's packet size - * @s: the AMDTP output stream + * amdtp_stream_get_max_payload - get the stream's packet size + * @s: the AMDTP stream * * This function must not be called before the stream has been configured - * with amdtp_out_stream_set_parameters(). + * with amdtp_stream_set_parameters(). */ -unsigned int amdtp_out_stream_get_max_payload(struct amdtp_out_stream *s) +unsigned int amdtp_stream_get_max_payload(struct amdtp_stream *s) { return 8 + s->syt_interval * s->data_block_quadlets * 4; } -EXPORT_SYMBOL(amdtp_out_stream_get_max_payload); +EXPORT_SYMBOL(amdtp_stream_get_max_payload); -static void amdtp_write_s16(struct amdtp_out_stream *s, +static void amdtp_write_s16(struct amdtp_stream *s, struct snd_pcm_substream *pcm, __be32 *buffer, unsigned int frames); -static void amdtp_write_s32(struct amdtp_out_stream *s, +static void amdtp_write_s32(struct amdtp_stream *s, struct snd_pcm_substream *pcm, __be32 *buffer, unsigned int frames); -static void amdtp_write_s16_dualwire(struct amdtp_out_stream *s, +static void amdtp_write_s16_dualwire(struct amdtp_stream *s, struct snd_pcm_substream *pcm, __be32 *buffer, unsigned int frames); -static void amdtp_write_s32_dualwire(struct amdtp_out_stream *s, +static void amdtp_write_s32_dualwire(struct amdtp_stream *s, struct snd_pcm_substream *pcm, __be32 *buffer, unsigned int frames); /** - * amdtp_out_stream_set_pcm_format - set the PCM format - * @s: the AMDTP output stream to configure + * amdtp_stream_set_pcm_format - set the PCM format + * @s: the AMDTP stream to configure * @format: the format of the ALSA PCM device * * The sample format must be set after the other paramters (rate/PCM channels/ * MIDI) and before the stream is started, and must not be changed while the * stream is running. */ -void amdtp_out_stream_set_pcm_format(struct amdtp_out_stream *s, - snd_pcm_format_t format) +void amdtp_stream_set_pcm_format(struct amdtp_stream *s, + snd_pcm_format_t format) { - if (WARN_ON(amdtp_out_stream_running(s))) + if (WARN_ON(amdtp_stream_running(s))) return; switch (format) { @@ -193,24 +193,24 @@ void amdtp_out_stream_set_pcm_format(struct amdtp_out_stream *s, break; } } -EXPORT_SYMBOL(amdtp_out_stream_set_pcm_format); +EXPORT_SYMBOL(amdtp_stream_set_pcm_format); /** - * amdtp_out_stream_pcm_prepare - prepare PCM device for running - * @s: the AMDTP output stream + * amdtp_stream_pcm_prepare - prepare PCM device for running + * @s: the AMDTP stream * * This function should be called from the PCM device's .prepare callback. */ -void amdtp_out_stream_pcm_prepare(struct amdtp_out_stream *s) +void amdtp_stream_pcm_prepare(struct amdtp_stream *s) { tasklet_kill(&s->period_tasklet); s->pcm_buffer_pointer = 0; s->pcm_period_pointer = 0; s->pointer_flush = true; } -EXPORT_SYMBOL(amdtp_out_stream_pcm_prepare); +EXPORT_SYMBOL(amdtp_stream_pcm_prepare); -static unsigned int calculate_data_blocks(struct amdtp_out_stream *s) +static unsigned int calculate_data_blocks(struct amdtp_stream *s) { unsigned int phase, data_blocks; @@ -243,7 +243,7 @@ static unsigned int calculate_data_blocks(struct amdtp_out_stream *s) return data_blocks; } -static unsigned int calculate_syt(struct amdtp_out_stream *s, +static unsigned int calculate_syt(struct amdtp_stream *s, unsigned int cycle) { unsigned int syt_offset, phase, index, syt; @@ -286,7 +286,7 @@ static unsigned int calculate_syt(struct amdtp_out_stream *s, } } -static void amdtp_write_s32(struct amdtp_out_stream *s, +static void amdtp_write_s32(struct amdtp_stream *s, struct snd_pcm_substream *pcm, __be32 *buffer, unsigned int frames) { @@ -312,7 +312,7 @@ static void amdtp_write_s32(struct amdtp_out_stream *s, } } -static void amdtp_write_s16(struct amdtp_out_stream *s, +static void amdtp_write_s16(struct amdtp_stream *s, struct snd_pcm_substream *pcm, __be32 *buffer, unsigned int frames) { @@ -338,7 +338,7 @@ static void amdtp_write_s16(struct amdtp_out_stream *s, } } -static void amdtp_write_s32_dualwire(struct amdtp_out_stream *s, +static void amdtp_write_s32_dualwire(struct amdtp_stream *s, struct snd_pcm_substream *pcm, __be32 *buffer, unsigned int frames) { @@ -369,7 +369,7 @@ static void amdtp_write_s32_dualwire(struct amdtp_out_stream *s, } } -static void amdtp_write_s16_dualwire(struct amdtp_out_stream *s, +static void amdtp_write_s16_dualwire(struct amdtp_stream *s, struct snd_pcm_substream *pcm, __be32 *buffer, unsigned int frames) { @@ -400,7 +400,7 @@ static void amdtp_write_s16_dualwire(struct amdtp_out_stream *s, } } -static void amdtp_fill_pcm_silence(struct amdtp_out_stream *s, +static void amdtp_fill_pcm_silence(struct amdtp_stream *s, __be32 *buffer, unsigned int frames) { unsigned int i, c; @@ -412,7 +412,7 @@ static void amdtp_fill_pcm_silence(struct amdtp_out_stream *s, } } -static void amdtp_fill_midi(struct amdtp_out_stream *s, +static void amdtp_fill_midi(struct amdtp_stream *s, __be32 *buffer, unsigned int frames) { unsigned int i; @@ -422,7 +422,7 @@ static void amdtp_fill_midi(struct amdtp_out_stream *s, cpu_to_be32(0x80000000); } -static void queue_out_packet(struct amdtp_out_stream *s, unsigned int cycle) +static void queue_out_packet(struct amdtp_stream *s, unsigned int cycle) { __be32 *buffer; unsigned int index, data_blocks, syt, ptr; @@ -473,7 +473,7 @@ static void queue_out_packet(struct amdtp_out_stream *s, unsigned int cycle) if (err < 0) { dev_err(&s->unit->device, "queueing error: %d\n", err); s->packet_index = -1; - amdtp_out_stream_pcm_abort(s); + amdtp_stream_pcm_abort(s); return; } @@ -501,7 +501,7 @@ static void queue_out_packet(struct amdtp_out_stream *s, unsigned int cycle) static void pcm_period_tasklet(unsigned long data) { - struct amdtp_out_stream *s = (void *)data; + struct amdtp_stream *s = (void *)data; struct snd_pcm_substream *pcm = ACCESS_ONCE(s->pcm); if (pcm) @@ -509,9 +509,9 @@ static void pcm_period_tasklet(unsigned long data) } static void out_packet_callback(struct fw_iso_context *context, u32 cycle, - size_t header_length, void *header, void *data) + size_t header_length, void *header, void *private_data) { - struct amdtp_out_stream *s = data; + struct amdtp_stream *s = private_data; unsigned int i, packets = header_length / 4; /* @@ -526,7 +526,7 @@ static void out_packet_callback(struct fw_iso_context *context, u32 cycle, fw_iso_context_queue_flush(s->context); } -static int queue_initial_skip_packets(struct amdtp_out_stream *s) +static int queue_initial_skip_packets(struct amdtp_stream *s) { struct fw_iso_packet skip_packet = { .skip = 1, @@ -548,16 +548,16 @@ static int queue_initial_skip_packets(struct amdtp_out_stream *s) } /** - * amdtp_out_stream_start - start sending packets - * @s: the AMDTP output stream to start + * amdtp_stream_start - start transferring packets + * @s: the AMDTP stream to start * @channel: the isochronous channel on the bus * @speed: firewire speed code * * The stream cannot be started until it has been configured with - * amdtp_out_stream_set_parameters() and amdtp_out_stream_set_pcm_format(), - * and it must be started before any PCM or MIDI device can be started. + * amdtp_stream_set_parameters() and it must be started before any PCM or MIDI + * device can be started. */ -int amdtp_out_stream_start(struct amdtp_out_stream *s, int channel, int speed) +int amdtp_stream_start(struct amdtp_stream *s, int channel, int speed) { static const struct { unsigned int data_block; @@ -575,7 +575,7 @@ int amdtp_out_stream_start(struct amdtp_out_stream *s, int channel, int speed) mutex_lock(&s->mutex); - if (WARN_ON(amdtp_out_stream_running(s) || + if (WARN_ON(amdtp_stream_running(s) || (!s->pcm_channels && !s->midi_ports))) { err = -EBADFD; goto err_unlock; @@ -586,7 +586,7 @@ int amdtp_out_stream_start(struct amdtp_out_stream *s, int channel, int speed) s->last_syt_offset = TICKS_PER_CYCLE; err = iso_packets_buffer_init(&s->buffer, s->unit, QUEUE_LENGTH, - amdtp_out_stream_get_max_payload(s), + amdtp_stream_get_max_payload(s), DMA_TO_DEVICE); if (err < 0) goto err_unlock; @@ -599,11 +599,11 @@ int amdtp_out_stream_start(struct amdtp_out_stream *s, int channel, int speed) err = PTR_ERR(s->context); if (err == -EBUSY) dev_err(&s->unit->device, - "no free output stream on this controller\n"); + "no free stream on this controller\n"); goto err_buffer; } - amdtp_out_stream_update(s); + amdtp_stream_update(s); s->packet_index = 0; s->data_block_counter = 0; @@ -629,15 +629,15 @@ err_unlock: return err; } -EXPORT_SYMBOL(amdtp_out_stream_start); +EXPORT_SYMBOL(amdtp_stream_start); /** - * amdtp_out_stream_pcm_pointer - get the PCM buffer position - * @s: the AMDTP output stream that transports the PCM data + * amdtp_stream_pcm_pointer - get the PCM buffer position + * @s: the AMDTP stream that transports the PCM data * * Returns the current buffer position, in frames. */ -unsigned long amdtp_out_stream_pcm_pointer(struct amdtp_out_stream *s) +unsigned long amdtp_stream_pcm_pointer(struct amdtp_stream *s) { /* this optimization is allowed to be racy */ if (s->pointer_flush) @@ -647,31 +647,31 @@ unsigned long amdtp_out_stream_pcm_pointer(struct amdtp_out_stream *s) return ACCESS_ONCE(s->pcm_buffer_pointer); } -EXPORT_SYMBOL(amdtp_out_stream_pcm_pointer); +EXPORT_SYMBOL(amdtp_stream_pcm_pointer); /** - * amdtp_out_stream_update - update the stream after a bus reset - * @s: the AMDTP output stream + * amdtp_stream_update - update the stream after a bus reset + * @s: the AMDTP stream */ -void amdtp_out_stream_update(struct amdtp_out_stream *s) +void amdtp_stream_update(struct amdtp_stream *s) { ACCESS_ONCE(s->source_node_id_field) = (fw_parent_device(s->unit)->card->node_id & 0x3f) << 24; } -EXPORT_SYMBOL(amdtp_out_stream_update); +EXPORT_SYMBOL(amdtp_stream_update); /** - * amdtp_out_stream_stop - stop sending packets - * @s: the AMDTP output stream to stop + * amdtp_stream_stop - stop sending packets + * @s: the AMDTP stream to stop * * All PCM and MIDI devices of the stream must be stopped before the stream * itself can be stopped. */ -void amdtp_out_stream_stop(struct amdtp_out_stream *s) +void amdtp_stream_stop(struct amdtp_stream *s) { mutex_lock(&s->mutex); - if (!amdtp_out_stream_running(s)) { + if (!amdtp_stream_running(s)) { mutex_unlock(&s->mutex); return; } @@ -684,16 +684,16 @@ void amdtp_out_stream_stop(struct amdtp_out_stream *s) mutex_unlock(&s->mutex); } -EXPORT_SYMBOL(amdtp_out_stream_stop); +EXPORT_SYMBOL(amdtp_stream_stop); /** - * amdtp_out_stream_pcm_abort - abort the running PCM device + * amdtp_stream_pcm_abort - abort the running PCM device * @s: the AMDTP stream about to be stopped * * If the isochronous stream needs to be stopped asynchronously, call this * function first to stop the PCM device. */ -void amdtp_out_stream_pcm_abort(struct amdtp_out_stream *s) +void amdtp_stream_pcm_abort(struct amdtp_stream *s) { struct snd_pcm_substream *pcm; @@ -705,4 +705,4 @@ void amdtp_out_stream_pcm_abort(struct amdtp_out_stream *s) snd_pcm_stream_unlock_irq(pcm); } } -EXPORT_SYMBOL(amdtp_out_stream_pcm_abort); +EXPORT_SYMBOL(amdtp_stream_pcm_abort); diff --git a/sound/firewire/amdtp.h b/sound/firewire/amdtp.h index 2746ecd..60028c7 100644 --- a/sound/firewire/amdtp.h +++ b/sound/firewire/amdtp.h @@ -8,7 +8,7 @@ #include "packets-buffer.h" /** - * enum cip_out_flags - describes details of the streaming protocol + * enum cip_flags - describes details of the streaming protocol * @CIP_NONBLOCKING: In non-blocking mode, each packet contains * sample_rate/8000 samples, with rounding up or down to adjust * for clock skew and left-over fractional samples. This should @@ -21,7 +21,7 @@ * two samples of a channel are stored consecutively in the packet. * Requires blocking mode and SYT_INTERVAL-aligned PCM buffer size. */ -enum cip_out_flags { +enum cip_flags { CIP_NONBLOCKING = 0x00, CIP_BLOCKING = 0x01, CIP_HI_DUALWIRE = 0x02, @@ -48,9 +48,9 @@ struct fw_unit; struct fw_iso_context; struct snd_pcm_substream; -struct amdtp_out_stream { +struct amdtp_stream { struct fw_unit *unit; - enum cip_out_flags flags; + enum cip_flags flags; struct fw_iso_context *context; struct mutex mutex; @@ -59,7 +59,7 @@ struct amdtp_out_stream { unsigned int data_block_quadlets; unsigned int pcm_channels; unsigned int midi_ports; - void (*transfer_samples)(struct amdtp_out_stream *s, + void (*transfer_samples)(struct amdtp_stream *s, struct snd_pcm_substream *pcm, __be32 *buffer, unsigned int frames); @@ -84,56 +84,62 @@ struct amdtp_out_stream { bool pointer_flush; }; -int amdtp_out_stream_init(struct amdtp_out_stream *s, struct fw_unit *unit, - enum cip_out_flags flags); -void amdtp_out_stream_destroy(struct amdtp_out_stream *s); +int amdtp_stream_init(struct amdtp_stream *s, struct fw_unit *unit, + enum cip_flags flags); +void amdtp_stream_destroy(struct amdtp_stream *s); -void amdtp_out_stream_set_parameters(struct amdtp_out_stream *s, - unsigned int rate, - unsigned int pcm_channels, - unsigned int midi_ports); -unsigned int amdtp_out_stream_get_max_payload(struct amdtp_out_stream *s); +void amdtp_stream_set_parameters(struct amdtp_stream *s, + unsigned int rate, + unsigned int pcm_channels, + unsigned int midi_ports); +unsigned int amdtp_stream_get_max_payload(struct amdtp_stream *s); -int amdtp_out_stream_start(struct amdtp_out_stream *s, int channel, int speed); -void amdtp_out_stream_update(struct amdtp_out_stream *s); -void amdtp_out_stream_stop(struct amdtp_out_stream *s); +int amdtp_stream_start(struct amdtp_stream *s, int channel, int speed); +void amdtp_stream_update(struct amdtp_stream *s); +void amdtp_stream_stop(struct amdtp_stream *s); -void amdtp_out_stream_set_pcm_format(struct amdtp_out_stream *s, - snd_pcm_format_t format); -void amdtp_out_stream_pcm_prepare(struct amdtp_out_stream *s); -unsigned long amdtp_out_stream_pcm_pointer(struct amdtp_out_stream *s); -void amdtp_out_stream_pcm_abort(struct amdtp_out_stream *s); +void amdtp_stream_set_pcm_format(struct amdtp_stream *s, + snd_pcm_format_t format); +void amdtp_stream_pcm_prepare(struct amdtp_stream *s); +unsigned long amdtp_stream_pcm_pointer(struct amdtp_stream *s); +void amdtp_stream_pcm_abort(struct amdtp_stream *s); extern const unsigned int amdtp_syt_intervals[CIP_SFC_COUNT]; -static inline bool amdtp_out_stream_running(struct amdtp_out_stream *s) +/** + * amdtp_stream_running - check stream is running or not + * @s: the AMDTP stream + * + * If this function returns true, the stream is running. + */ +static inline bool amdtp_stream_running(struct amdtp_stream *s) { return !IS_ERR(s->context); } /** - * amdtp_out_streaming_error - check for streaming error - * @s: the AMDTP output stream + * amdtp_streaming_error - check for streaming error + * @s: the AMDTP stream * * If this function returns true, the stream's packet queue has stopped due to * an asynchronous error. */ -static inline bool amdtp_out_streaming_error(struct amdtp_out_stream *s) +static inline bool amdtp_streaming_error(struct amdtp_stream *s) { return s->packet_index < 0; } /** - * amdtp_out_stream_pcm_trigger - start/stop playback from a PCM device - * @s: the AMDTP output stream + * amdtp_stream_pcm_trigger - start/stop playback from a PCM device + * @s: the AMDTP stream * @pcm: the PCM device to be started, or %NULL to stop the current device * * Call this function on a running isochronous stream to enable the actual * transmission of PCM data. This function should be called from the PCM * device's .trigger callback. */ -static inline void amdtp_out_stream_pcm_trigger(struct amdtp_out_stream *s, - struct snd_pcm_substream *pcm) +static inline void amdtp_stream_pcm_trigger(struct amdtp_stream *s, + struct snd_pcm_substream *pcm) { ACCESS_ONCE(s->pcm) = pcm; } diff --git a/sound/firewire/dice.c b/sound/firewire/dice.c index c0aa649..88814cc 100644 --- a/sound/firewire/dice.c +++ b/sound/firewire/dice.c @@ -51,7 +51,7 @@ struct dice { wait_queue_head_t hwdep_wait; u32 notification_bits; struct fw_iso_resources resources; - struct amdtp_out_stream stream; + struct amdtp_stream stream; }; MODULE_DESCRIPTION("DICE driver"); @@ -460,17 +460,17 @@ static int dice_stream_start_packets(struct dice *dice) { int err; - if (amdtp_out_stream_running(&dice->stream)) + if (amdtp_stream_running(&dice->stream)) return 0; - err = amdtp_out_stream_start(&dice->stream, dice->resources.channel, - fw_parent_device(dice->unit)->max_speed); + err = amdtp_stream_start(&dice->stream, dice->resources.channel, + fw_parent_device(dice->unit)->max_speed); if (err < 0) return err; err = dice_enable_set(dice); if (err < 0) { - amdtp_out_stream_stop(&dice->stream); + amdtp_stream_stop(&dice->stream); return err; } @@ -484,7 +484,7 @@ static int dice_stream_start(struct dice *dice) if (!dice->resources.allocated) { err = fw_iso_resources_allocate(&dice->resources, - amdtp_out_stream_get_max_payload(&dice->stream), + amdtp_stream_get_max_payload(&dice->stream), fw_parent_device(dice->unit)->max_speed); if (err < 0) goto error; @@ -516,9 +516,9 @@ error: static void dice_stream_stop_packets(struct dice *dice) { - if (amdtp_out_stream_running(&dice->stream)) { + if (amdtp_stream_running(&dice->stream)) { dice_enable_clear(dice); - amdtp_out_stream_stop(&dice->stream); + amdtp_stream_stop(&dice->stream); } } @@ -581,12 +581,12 @@ static int dice_hw_params(struct snd_pcm_substream *substream, return err; mode = rate_index_to_mode(rate_index); - amdtp_out_stream_set_parameters(&dice->stream, - params_rate(hw_params), - params_channels(hw_params), - dice->rx_midi_ports[mode]); - amdtp_out_stream_set_pcm_format(&dice->stream, - params_format(hw_params)); + amdtp_stream_set_parameters(&dice->stream, + params_rate(hw_params), + params_channels(hw_params), + dice->rx_midi_ports[mode]); + amdtp_stream_set_pcm_format(&dice->stream, + params_format(hw_params)); return 0; } @@ -609,7 +609,7 @@ static int dice_prepare(struct snd_pcm_substream *substream) mutex_lock(&dice->mutex); - if (amdtp_out_streaming_error(&dice->stream)) + if (amdtp_streaming_error(&dice->stream)) dice_stream_stop_packets(dice); err = dice_stream_start(dice); @@ -620,7 +620,7 @@ static int dice_prepare(struct snd_pcm_substream *substream) mutex_unlock(&dice->mutex); - amdtp_out_stream_pcm_prepare(&dice->stream); + amdtp_stream_pcm_prepare(&dice->stream); return 0; } @@ -640,7 +640,7 @@ static int dice_trigger(struct snd_pcm_substream *substream, int cmd) default: return -EINVAL; } - amdtp_out_stream_pcm_trigger(&dice->stream, pcm); + amdtp_stream_pcm_trigger(&dice->stream, pcm); return 0; } @@ -649,7 +649,7 @@ static snd_pcm_uframes_t dice_pointer(struct snd_pcm_substream *substream) { struct dice *dice = substream->private_data; - return amdtp_out_stream_pcm_pointer(&dice->stream); + return amdtp_stream_pcm_pointer(&dice->stream); } static int dice_create_pcm(struct dice *dice) @@ -1104,7 +1104,7 @@ static void dice_card_free(struct snd_card *card) { struct dice *dice = card->private_data; - amdtp_out_stream_destroy(&dice->stream); + amdtp_stream_destroy(&dice->stream); fw_core_remove_address_handler(&dice->notification_handler); mutex_destroy(&dice->mutex); } @@ -1360,8 +1360,8 @@ static int dice_probe(struct fw_unit *unit, const struct ieee1394_device_id *id) goto err_owner; dice->resources.channels_mask = 0x00000000ffffffffuLL; - err = amdtp_out_stream_init(&dice->stream, unit, - CIP_BLOCKING | CIP_HI_DUALWIRE); + err = amdtp_stream_init(&dice->stream, unit, + CIP_BLOCKING | CIP_HI_DUALWIRE); if (err < 0) goto err_resources; @@ -1417,7 +1417,7 @@ static void dice_remove(struct fw_unit *unit) { struct dice *dice = dev_get_drvdata(&unit->device); - amdtp_out_stream_pcm_abort(&dice->stream); + amdtp_stream_pcm_abort(&dice->stream); snd_card_disconnect(dice->card); @@ -1443,7 +1443,7 @@ static void dice_bus_reset(struct fw_unit *unit) * to stop so that the application can restart them in an orderly * manner. */ - amdtp_out_stream_pcm_abort(&dice->stream); + amdtp_stream_pcm_abort(&dice->stream); mutex_lock(&dice->mutex); diff --git a/sound/firewire/speakers.c b/sound/firewire/speakers.c index cc8bc3a..554818e 100644 --- a/sound/firewire/speakers.c +++ b/sound/firewire/speakers.c @@ -51,7 +51,7 @@ struct fwspk { const struct device_info *device_info; struct mutex mutex; struct cmp_connection connection; - struct amdtp_out_stream stream; + struct amdtp_stream stream; bool mute; s16 volume[6]; s16 volume_min; @@ -187,8 +187,8 @@ static int fwspk_close(struct snd_pcm_substream *substream) static void fwspk_stop_stream(struct fwspk *fwspk) { - if (amdtp_out_stream_running(&fwspk->stream)) { - amdtp_out_stream_stop(&fwspk->stream); + if (amdtp_stream_running(&fwspk->stream)) { + amdtp_stream_stop(&fwspk->stream); cmp_connection_break(&fwspk->connection); } } @@ -244,13 +244,13 @@ static int fwspk_hw_params(struct snd_pcm_substream *substream, if (err < 0) goto error; - amdtp_out_stream_set_parameters(&fwspk->stream, - params_rate(hw_params), - params_channels(hw_params), - 0); + amdtp_stream_set_parameters(&fwspk->stream, + params_rate(hw_params), + params_channels(hw_params), + 0); - amdtp_out_stream_set_pcm_format(&fwspk->stream, - params_format(hw_params)); + amdtp_stream_set_pcm_format(&fwspk->stream, + params_format(hw_params)); err = fwspk_set_rate(fwspk, fwspk->stream.sfc); if (err < 0) @@ -282,25 +282,25 @@ static int fwspk_prepare(struct snd_pcm_substream *substream) mutex_lock(&fwspk->mutex); - if (amdtp_out_streaming_error(&fwspk->stream)) + if (amdtp_streaming_error(&fwspk->stream)) fwspk_stop_stream(fwspk); - if (!amdtp_out_stream_running(&fwspk->stream)) { + if (!amdtp_stream_running(&fwspk->stream)) { err = cmp_connection_establish(&fwspk->connection, - amdtp_out_stream_get_max_payload(&fwspk->stream)); + amdtp_stream_get_max_payload(&fwspk->stream)); if (err < 0) goto err_mutex; - err = amdtp_out_stream_start(&fwspk->stream, - fwspk->connection.resources.channel, - fwspk->connection.speed); + err = amdtp_stream_start(&fwspk->stream, + fwspk->connection.resources.channel, + fwspk->connection.speed); if (err < 0) goto err_connection; } mutex_unlock(&fwspk->mutex); - amdtp_out_stream_pcm_prepare(&fwspk->stream); + amdtp_stream_pcm_prepare(&fwspk->stream); return 0; @@ -327,7 +327,7 @@ static int fwspk_trigger(struct snd_pcm_substream *substream, int cmd) default: return -EINVAL; } - amdtp_out_stream_pcm_trigger(&fwspk->stream, pcm); + amdtp_stream_pcm_trigger(&fwspk->stream, pcm); return 0; } @@ -335,7 +335,7 @@ static snd_pcm_uframes_t fwspk_pointer(struct snd_pcm_substream *substream) { struct fwspk *fwspk = substream->private_data; - return amdtp_out_stream_pcm_pointer(&fwspk->stream); + return amdtp_stream_pcm_pointer(&fwspk->stream); } static int fwspk_create_pcm(struct fwspk *fwspk) @@ -653,7 +653,7 @@ static void fwspk_card_free(struct snd_card *card) { struct fwspk *fwspk = card->private_data; - amdtp_out_stream_destroy(&fwspk->stream); + amdtp_stream_destroy(&fwspk->stream); cmp_connection_destroy(&fwspk->connection); fw_unit_put(fwspk->unit); mutex_destroy(&fwspk->mutex); @@ -683,7 +683,7 @@ static int fwspk_probe(struct fw_unit *unit, if (err < 0) goto err_unit; - err = amdtp_out_stream_init(&fwspk->stream, unit, CIP_NONBLOCKING); + err = amdtp_stream_init(&fwspk->stream, unit, CIP_NONBLOCKING); if (err < 0) goto err_connection; @@ -733,21 +733,21 @@ static void fwspk_bus_reset(struct fw_unit *unit) fcp_bus_reset(fwspk->unit); if (cmp_connection_update(&fwspk->connection) < 0) { - amdtp_out_stream_pcm_abort(&fwspk->stream); + amdtp_stream_pcm_abort(&fwspk->stream); mutex_lock(&fwspk->mutex); fwspk_stop_stream(fwspk); mutex_unlock(&fwspk->mutex); return; } - amdtp_out_stream_update(&fwspk->stream); + amdtp_stream_update(&fwspk->stream); } static void fwspk_remove(struct fw_unit *unit) { struct fwspk *fwspk = dev_get_drvdata(&unit->device); - amdtp_out_stream_pcm_abort(&fwspk->stream); + amdtp_stream_pcm_abort(&fwspk->stream); snd_card_disconnect(fwspk->card); mutex_lock(&fwspk->mutex); -- 1.8.3.2 |
From: Takashi S. <o-t...@sa...> - 2014-01-29 13:45:30
|
For capturing PCM, this commit adds the functionality to handle in-stream. This is also applied for dual-wire mode. Currently, capturing 32bit samples are supported. Signed-off-by: Takashi Sakamoto <o-t...@sa...> --- sound/firewire/amdtp.c | 218 +++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 201 insertions(+), 17 deletions(-) diff --git a/sound/firewire/amdtp.c b/sound/firewire/amdtp.c index ca79cb4..4ebfd67 100644 --- a/sound/firewire/amdtp.c +++ b/sound/firewire/amdtp.c @@ -39,6 +39,7 @@ * only "Clock-based rate control mode" is supported */ #define AMDTP_FDF_AM824 (0 << (CIP_FDF_SFC_SHIFT + 3)) +#define AMDTP_FDF_NO_DATA 0xff #define AMDTP_DBS_MASK 0x00ff0000 #define AMDTP_DBS_SHIFT 16 #define AMDTP_DBC_MASK 0x000000ff @@ -47,6 +48,7 @@ #define INTERRUPT_INTERVAL 16 #define QUEUE_LENGTH 48 +#define IN_PACKET_HEADER_SIZE 4 #define OUT_PACKET_HEADER_SIZE 0 static void pcm_period_tasklet(unsigned long data); @@ -179,6 +181,12 @@ static void amdtp_write_s16_dualwire(struct amdtp_stream *s, static void amdtp_write_s32_dualwire(struct amdtp_stream *s, struct snd_pcm_substream *pcm, __be32 *buffer, unsigned int frames); +static void amdtp_read_s32(struct amdtp_stream *s, + struct snd_pcm_substream *pcm, + __be32 *buffer, unsigned int frames); +static void amdtp_read_s32_dualwire(struct amdtp_stream *s, + struct snd_pcm_substream *pcm, + __be32 *buffer, unsigned int frames); /** * amdtp_stream_set_pcm_format - set the PCM format @@ -200,16 +208,26 @@ void amdtp_stream_set_pcm_format(struct amdtp_stream *s, WARN_ON(1); /* fall through */ case SNDRV_PCM_FORMAT_S16: - if (s->dual_wire) - s->transfer_samples = amdtp_write_s16_dualwire; - else - s->transfer_samples = amdtp_write_s16; - break; + if (s->direction == AMDTP_OUT_STREAM) { + if (s->dual_wire) + s->transfer_samples = amdtp_write_s16_dualwire; + else + s->transfer_samples = amdtp_write_s16; + break; + } + WARN_ON(1); + /* fall through */ case SNDRV_PCM_FORMAT_S32: - if (s->dual_wire) - s->transfer_samples = amdtp_write_s32_dualwire; - else - s->transfer_samples = amdtp_write_s32; + if (s->direction == AMDTP_OUT_STREAM) { + if (s->dual_wire) + s->transfer_samples = amdtp_write_s32_dualwire; + else + s->transfer_samples = amdtp_write_s32; + } else if (s->dual_wire) { + s->transfer_samples = amdtp_read_s32_dualwire; + } else { + s->transfer_samples = amdtp_read_s32; + } break; } } @@ -420,6 +438,58 @@ static void amdtp_write_s16_dualwire(struct amdtp_stream *s, } } +static void amdtp_read_s32(struct amdtp_stream *s, + struct snd_pcm_substream *pcm, + __be32 *buffer, unsigned int frames) +{ + struct snd_pcm_runtime *runtime = pcm->runtime; + unsigned int remaining_frames, i, c; + u32 *dst; + + dst = (void *)runtime->dma_area + + frames_to_bytes(runtime, s->pcm_buffer_pointer); + remaining_frames = runtime->buffer_size - s->pcm_buffer_pointer; + + for (i = 0; i < frames; ++i) { + for (c = 0; c < s->pcm_channels; ++c) { + *dst = be32_to_cpu(buffer[c]) << 8; + dst++; + } + buffer += s->data_block_quadlets; + if (--remaining_frames == 0) + dst = (void *)runtime->dma_area; + } +} + +static void amdtp_read_s32_dualwire(struct amdtp_stream *s, + struct snd_pcm_substream *pcm, + __be32 *buffer, unsigned int frames) +{ + struct snd_pcm_runtime *runtime = pcm->runtime; + unsigned int channels, remaining_frames, i, c; + u32 *dst; + + dst = (void *)runtime->dma_area + + frames_to_bytes(runtime, s->pcm_buffer_pointer); + remaining_frames = runtime->buffer_size - s->pcm_buffer_pointer; + channels = s->pcm_channels / 2; + + for (i = 0; i < frames; ++i) { + for (c = 0; c < channels; ++c) { + *dst = be32_to_cpu(buffer[c * 2]) << 8; + dst++; + } + buffer += 1; + for (c = 0; c < channels; ++c) { + *dst = be32_to_cpu(buffer[c * 2]) << 8; + dst++; + } + buffer += s->data_block_quadlets - 1; + if (--remaining_frames == 0) + dst = (void *)runtime->dma_area; + } +} + static void amdtp_fill_pcm_silence(struct amdtp_stream *s, __be32 *buffer, unsigned int frames) { @@ -505,6 +575,12 @@ static inline int queue_out_packet(struct amdtp_stream *s, payload_length, skip); } +static inline int queue_in_packet(struct amdtp_stream *s) +{ + return queue_packet(s, IN_PACKET_HEADER_SIZE, + amdtp_stream_get_max_payload(s), false); +} + static void handle_out_packet(struct amdtp_stream *s, unsigned int cycle) { __be32 *buffer; @@ -552,6 +628,67 @@ static void handle_out_packet(struct amdtp_stream *s, unsigned int cycle) update_pcm_pointers(s, pcm, data_blocks); } +static void handle_in_packet(struct amdtp_stream *s, + unsigned int payload_quadlets, + __be32 *buffer) +{ + u32 cip_header[2]; + unsigned int data_block_quadlets, data_blocks, data_block_counter; + struct snd_pcm_substream *pcm; + + cip_header[0] = be32_to_cpu(buffer[0]); + cip_header[1] = be32_to_cpu(buffer[1]); + + /* + * This module supports 'Two-quadlet CIP header with SYT field'. + * For convinience, also check FMT field is AM824 or not. + */ + if (((cip_header[0] & CIP_EOH_MASK) == CIP_EOH) || + ((cip_header[1] & CIP_EOH_MASK) != CIP_EOH) || + ((cip_header[1] & CIP_FMT_MASK) != CIP_FMT_AM)) { + dev_info_ratelimited(&s->unit->device, + "Invalid CIP header for AMDTP: %08X:%08X\n", + cip_header[0], cip_header[1]); + return; + } + + /* ignore empty CIP packet or NO-DATA AMDTP packet */ + if ((payload_quadlets < 3) || + (((cip_header[1] & CIP_FDF_MASK) >> CIP_FDF_SFC_SHIFT) == + AMDTP_FDF_NO_DATA)) + return; + + data_block_quadlets = + (cip_header[1] & AMDTP_DBS_MASK) >> AMDTP_DBS_SHIFT; + /* avoid division by zero */ + if (data_block_quadlets == 0) { + dev_info_ratelimited(&s->unit->device, + "Detect invalid value in dbs field: %08X\n", + data_block_quadlets); + return; + } + + data_blocks = (payload_quadlets - 2) / data_block_quadlets; + data_block_counter = cip_header[1] & AMDTP_DBC_MASK; + + /* check packet continuity */ + s->data_block_counter = (s->data_block_counter + data_blocks) & 0xff; + if (s->data_block_counter != data_block_counter) { + dev_info_ratelimited(&s->unit->device, + "Detect uncontinuity of CIP packets\n"); + s->data_block_counter = data_block_counter; + return; + } + + buffer += 2; + + pcm = ACCESS_ONCE(s->pcm); + if (pcm) { + s->transfer_samples(s, pcm, buffer, data_blocks); + update_pcm_pointers(s, pcm, data_blocks); + } +} + static void out_stream_callback(struct fw_iso_context *context, u32 cycle, size_t header_length, void *header, void *private_data) @@ -571,6 +708,35 @@ static void out_stream_callback(struct fw_iso_context *context, u32 cycle, fw_iso_context_queue_flush(s->context); } +static void in_stream_callback(struct fw_iso_context *context, u32 cycle, + size_t header_length, void *header, + void *private_data) +{ + struct amdtp_stream *s = private_data; + unsigned int p, packets, data_quadlets; + __be32 *buffer, *headers = header; + + /* The number of packets in buffer */ + packets = header_length / IN_PACKET_HEADER_SIZE; + + for (p = 0; p < packets; p++) { + buffer = s->buffer.packets[s->packet_index].buffer; + + /* The number of quadlets in this packet */ + data_quadlets = + (be32_to_cpu(headers[p]) >> ISO_DATA_LENGTH_SHIFT) / 4; + /* handle each packet data */ + handle_in_packet(s, data_quadlets, buffer); + + if (queue_in_packet(s) < 0) { + amdtp_stream_pcm_abort(s); + return; + } + } + + fw_iso_context_queue_flush(s->context); +} + /** * amdtp_stream_start - start transferring packets * @s: the AMDTP stream to start @@ -595,7 +761,10 @@ int amdtp_stream_start(struct amdtp_stream *s, int channel, int speed) [CIP_SFC_88200] = { 0, 67 }, [CIP_SFC_176400] = { 0, 67 }, }; - int err; + unsigned int header_size; + enum dma_data_direction dir; + fw_iso_callback_t cb; + int type, err; mutex_lock(&s->mutex); @@ -609,16 +778,26 @@ int amdtp_stream_start(struct amdtp_stream *s, int channel, int speed) s->syt_offset_state = initial_state[s->sfc].syt_offset; s->last_syt_offset = TICKS_PER_CYCLE; + /* initialize packet buffer */ + if (s->direction == AMDTP_IN_STREAM) { + dir = DMA_FROM_DEVICE; + type = FW_ISO_CONTEXT_RECEIVE; + header_size = IN_PACKET_HEADER_SIZE; + cb = in_stream_callback; + } else { + dir = DMA_TO_DEVICE; + type = FW_ISO_CONTEXT_TRANSMIT; + header_size = OUT_PACKET_HEADER_SIZE; + cb = out_stream_callback; + } err = iso_packets_buffer_init(&s->buffer, s->unit, QUEUE_LENGTH, - amdtp_stream_get_max_payload(s), - DMA_TO_DEVICE); + amdtp_stream_get_max_payload(s), dir); if (err < 0) goto err_unlock; s->context = fw_iso_context_create(fw_parent_device(s->unit)->card, - FW_ISO_CONTEXT_TRANSMIT, - channel, speed, 0, - out_stream_callback, s); + type, channel, speed, header_size, + cb, s); if (IS_ERR(s->context)) { err = PTR_ERR(s->context); if (err == -EBUSY) @@ -631,13 +810,18 @@ int amdtp_stream_start(struct amdtp_stream *s, int channel, int speed) s->packet_index = 0; do { - err = queue_out_packet(s, 0, true); + if (s->direction == AMDTP_IN_STREAM) + err = queue_in_packet(s); + else + err = queue_out_packet(s, 0, true); if (err < 0) goto err_context; } while (s->packet_index > 0); + /* NOTE: TAG1 matches CIP. This just affects in stream */ s->data_block_counter = 0; - err = fw_iso_context_start(s->context, -1, 0, 0); + err = fw_iso_context_start(s->context, -1, 0, + FW_ISO_CONTEXT_MATCH_TAG1); if (err < 0) goto err_context; -- 1.8.3.2 |
From: Takashi S. <o-t...@sa...> - 2014-01-29 13:45:33
|
Generally, the devices can synchronize to handle 'presentation timestamp' in CIP packets. This commit adds functionality to pick up this timestamp from in-packets transmitted by the device, then use it for out packets. In current implementation, this module generated the timestamp by itself. This is 'SYT Match' mode. Then this drive acts as synchronization master. This commit allows this module to act as synchronization slave. This commit restricts this mechanism is only available in blocking mode because handling the timestamp in non-blocking mode is more complicated than in blocking mode. Signed-off-by: Takashi Sakamoto <o-t...@sa...> --- sound/firewire/amdtp.c | 80 ++++++++++++++++++++++++++++++++++++++++++++++---- sound/firewire/amdtp.h | 32 ++++++++++++++++++-- 2 files changed, 103 insertions(+), 9 deletions(-) diff --git a/sound/firewire/amdtp.c b/sound/firewire/amdtp.c index 56b01c8..8284b39 100644 --- a/sound/firewire/amdtp.c +++ b/sound/firewire/amdtp.c @@ -11,6 +11,7 @@ #include <linux/firewire.h> #include <linux/module.h> #include <linux/slab.h> +#include <linux/sched.h> #include <sound/pcm.h> #include <sound/rawmidi.h> #include "amdtp.h" @@ -72,6 +73,10 @@ int amdtp_stream_init(struct amdtp_stream *s, struct fw_unit *unit, tasklet_init(&s->period_tasklet, pcm_period_tasklet, (unsigned long)s); s->packet_index = 0; + init_waitqueue_head(&s->callback_wait); + s->callbacked = false; + s->sync_slave = ERR_PTR(-1); + return 0; } EXPORT_SYMBOL(amdtp_stream_init); @@ -626,7 +631,7 @@ static void handle_out_packet(struct amdtp_stream *s, unsigned int syt) { __be32 *buffer; unsigned int data_blocks, payload_length; - struct snd_pcm_substream *pcm; + struct snd_pcm_substream *pcm = NULL; if (s->packet_index < 0) return; @@ -756,7 +761,7 @@ static void in_stream_callback(struct fw_iso_context *context, u32 cycle, void *private_data) { struct amdtp_stream *s = private_data; - unsigned int p, packets, data_quadlets; + unsigned int p, packets, syt, data_quadlets; __be32 *buffer, *headers = header; /* The number of packets in buffer */ @@ -768,6 +773,15 @@ static void in_stream_callback(struct fw_iso_context *context, u32 cycle, /* The number of quadlets in this packet */ data_quadlets = (be32_to_cpu(headers[p]) >> ISO_DATA_LENGTH_SHIFT) / 4; + + /* Process sync slave stream */ + if ((s->flags & CIP_BLOCKING) && + (s->flags & CIP_SYNC_TO_DEVICE) && + s->sync_slave->callbacked) { + syt = be32_to_cpu(buffer[1]) & CIP_SYT_MASK; + handle_out_packet(s->sync_slave, syt); + } + /* handle each packet data */ handle_in_packet(s, data_quadlets, buffer); @@ -777,9 +791,48 @@ static void in_stream_callback(struct fw_iso_context *context, u32 cycle, } } + /* when sync to device, flush the packets for slave stream */ + if ((s->flags & CIP_BLOCKING) && + (s->flags & CIP_SYNC_TO_DEVICE) && s->sync_slave->callbacked) + fw_iso_context_queue_flush(s->sync_slave->context); + fw_iso_context_queue_flush(s->context); } +/* processing is done by master callback */ +static void slave_stream_callback(struct fw_iso_context *context, u32 cycle, + size_t header_length, void *header, + void *private_data) +{ + return; +} + +/* this is executed one time */ +static void amdtp_stream_callback(struct fw_iso_context *context, u32 cycle, + size_t header_length, void *header, + void *private_data) +{ + struct amdtp_stream *s = private_data; + + /* + * For in-stream, first packet has come. + * For out-stream, prepared to transmit first packet + */ + s->callbacked = true; + wake_up(&s->callback_wait); + + if (s->direction == AMDTP_IN_STREAM) + context->callback.sc = in_stream_callback; + else if ((s->flags & CIP_BLOCKING) && (s->flags & CIP_SYNC_TO_DEVICE)) + context->callback.sc = slave_stream_callback; + else + context->callback.sc = out_stream_callback; + + context->callback.sc(context, cycle, header_length, header, s); + + return; +} + /** * amdtp_stream_start - start transferring packets * @s: the AMDTP stream to start @@ -806,7 +859,6 @@ int amdtp_stream_start(struct amdtp_stream *s, int channel, int speed) }; unsigned int header_size; enum dma_data_direction dir; - fw_iso_callback_t cb; int type, err; mutex_lock(&s->mutex); @@ -826,12 +878,10 @@ int amdtp_stream_start(struct amdtp_stream *s, int channel, int speed) dir = DMA_FROM_DEVICE; type = FW_ISO_CONTEXT_RECEIVE; header_size = IN_PACKET_HEADER_SIZE; - cb = in_stream_callback; } else { dir = DMA_TO_DEVICE; type = FW_ISO_CONTEXT_TRANSMIT; header_size = OUT_PACKET_HEADER_SIZE; - cb = out_stream_callback; } err = iso_packets_buffer_init(&s->buffer, s->unit, QUEUE_LENGTH, amdtp_stream_get_max_payload(s), dir); @@ -840,7 +890,7 @@ int amdtp_stream_start(struct amdtp_stream *s, int channel, int speed) s->context = fw_iso_context_create(fw_parent_device(s->unit)->card, type, channel, speed, header_size, - cb, s); + amdtp_stream_callback, s); if (IS_ERR(s->context)) { err = PTR_ERR(s->context); if (err == -EBUSY) @@ -863,6 +913,7 @@ int amdtp_stream_start(struct amdtp_stream *s, int channel, int speed) /* NOTE: TAG1 matches CIP. This just affects in stream */ s->data_block_counter = 0; + s->callbacked = false; err = fw_iso_context_start(s->context, -1, 0, FW_ISO_CONTEXT_MATCH_TAG1); if (err < 0) @@ -935,6 +986,8 @@ void amdtp_stream_stop(struct amdtp_stream *s) s->context = ERR_PTR(-1); iso_packets_buffer_destroy(&s->buffer, s->unit); + s->callbacked = false; + mutex_unlock(&s->mutex); } EXPORT_SYMBOL(amdtp_stream_stop); @@ -959,3 +1012,18 @@ void amdtp_stream_pcm_abort(struct amdtp_stream *s) } } EXPORT_SYMBOL(amdtp_stream_pcm_abort); + +/** + * amdtp_stream_wait_callback - sleep till callbacked or timeout + * @s: the AMDTP stream + * @timeout: msec till timeout + * + * If this function return false, the AMDTP stream should be stopped. + */ +bool amdtp_stream_wait_callback(struct amdtp_stream *s, unsigned int timeout) +{ + return wait_event_timeout(s->callback_wait, + s->callbacked == true, + msecs_to_jiffies(timeout)) > 0; +} +EXPORT_SYMBOL(amdtp_stream_wait_callback); diff --git a/sound/firewire/amdtp.h b/sound/firewire/amdtp.h index 96f25f9..0e73313 100644 --- a/sound/firewire/amdtp.h +++ b/sound/firewire/amdtp.h @@ -20,11 +20,14 @@ * at half the actual sample rate with twice the number of channels; * two samples of a channel are stored consecutively in the packet. * Requires blocking mode and SYT_INTERVAL-aligned PCM buffer size. + * @CIP_SYNC_TO_DEVICE: In sync to device mode, time stamp in out packets is + * generated by in packets. Defaultly this driver generates timestamp. */ enum cip_flags { - CIP_NONBLOCKING = 0x00, - CIP_BLOCKING = 0x01, - CIP_HI_DUALWIRE = 0x02, + CIP_NONBLOCKING = 0x00, + CIP_BLOCKING = 0x01, + CIP_HI_DUALWIRE = 0x02, + CIP_SYNC_TO_DEVICE = 0x04 }; /** @@ -102,6 +105,10 @@ struct amdtp_stream { bool pointer_flush; struct snd_rawmidi_substream *midi[AMDTP_MAX_CHANNELS_FOR_MIDI * 8]; + + bool callbacked; + wait_queue_head_t callback_wait; + struct amdtp_stream *sync_slave; }; int amdtp_stream_init(struct amdtp_stream *s, struct fw_unit *unit, @@ -124,6 +131,7 @@ void amdtp_stream_set_pcm_format(struct amdtp_stream *s, void amdtp_stream_pcm_prepare(struct amdtp_stream *s); unsigned long amdtp_stream_pcm_pointer(struct amdtp_stream *s); void amdtp_stream_pcm_abort(struct amdtp_stream *s); +bool amdtp_stream_wait_callback(struct amdtp_stream *s, unsigned int timeout); extern const unsigned int amdtp_syt_intervals[CIP_SFC_COUNT]; @@ -199,4 +207,22 @@ static inline bool cip_sfc_is_base_44100(enum cip_sfc sfc) return sfc & 1; } +static inline void amdtp_stream_set_sync(enum cip_flags sync_mode, + struct amdtp_stream *master, + struct amdtp_stream *slave) +{ + /* clear sync flag */ + master->flags &= ~CIP_SYNC_TO_DEVICE; + slave->flags &= ~CIP_SYNC_TO_DEVICE; + + if (sync_mode == CIP_SYNC_TO_DEVICE) { + master->flags |= CIP_SYNC_TO_DEVICE; + slave->flags |= CIP_SYNC_TO_DEVICE; + master->sync_slave = slave; + } else + master->sync_slave = ERR_PTR(-1); + + slave->sync_slave = ERR_PTR(-1); +} + #endif -- 1.8.3.2 |
From: Takashi S. <o-t...@sa...> - 2014-01-29 13:45:34
|
This commit is to assure the continuity of timestamp in in-packets transmitted by devices. Fireworks with firmware version 5.5 or former is a type of device which transmits packets with a bit disorder. This commit add sorting of in-packets. When callback of receive-context is processed, the parameters of in-packets are stored in sort table and sorted by its value of data block counter. The sort algorism works faster in ordered sequence than in messy sequence. This is convinient for this purpose because the sequence is assumed not to be so messy. After sorting, packets are processed except for a last few packets in sort table. These packets are stored in buffer once and used in next cycle. Signed-off-by: Takashi Sakamoto <o-t...@sa...> --- sound/firewire/amdtp.c | 130 +++++++++++++++++++++++++++++++++++++++++++------ sound/firewire/amdtp.h | 4 ++ 2 files changed, 119 insertions(+), 15 deletions(-) diff --git a/sound/firewire/amdtp.c b/sound/firewire/amdtp.c index 8284b39..9d1c47f 100644 --- a/sound/firewire/amdtp.c +++ b/sound/firewire/amdtp.c @@ -45,6 +45,7 @@ #define AMDTP_DBS_MASK 0x00ff0000 #define AMDTP_DBS_SHIFT 16 #define AMDTP_DBC_MASK 0x000000ff +#define DBC_THRESHOLD (AMDTP_DBC_MASK / 2) /* TODO: make these configurable */ #define INTERRUPT_INTERVAL 16 @@ -53,6 +54,13 @@ #define IN_PACKET_HEADER_SIZE 4 #define OUT_PACKET_HEADER_SIZE 0 +/* for re-ordering receive packets */ +struct sort_table { + unsigned int id; + unsigned int dbc; + unsigned int payload_size; +}; + static void pcm_period_tasklet(unsigned long data); /** @@ -77,6 +85,9 @@ int amdtp_stream_init(struct amdtp_stream *s, struct fw_unit *unit, s->callbacked = false; s->sync_slave = ERR_PTR(-1); + s->sort_table = NULL; + s->left_packets = NULL; + return 0; } EXPORT_SYMBOL(amdtp_stream_init); @@ -735,6 +746,44 @@ static void handle_in_packet(struct amdtp_stream *s, update_pcm_pointers(s, pcm, data_blocks); } +#define SWAP(tbl, m, n) \ + t = tbl[n].id; \ + tbl[n].id = tbl[m].id; \ + tbl[m].id = t; \ + t = tbl[n].dbc; \ + tbl[n].dbc = tbl[m].dbc; \ + tbl[m].dbc = t; \ + t = tbl[n].payload_size; \ + tbl[n].payload_size = tbl[m].payload_size; \ + tbl[m].payload_size = t; +static void packet_sort(struct sort_table *tbl, unsigned int len) +{ + unsigned int i, j, k, t; + + i = 0; + do { + for (j = i + 1; j < len; j++) { + if (((tbl[i].dbc > tbl[j].dbc) && + (tbl[i].dbc - tbl[j].dbc < DBC_THRESHOLD))) { + SWAP(tbl, i, j); + } else if ((tbl[j].dbc > tbl[i].dbc) && + (tbl[j].dbc - tbl[i].dbc > + DBC_THRESHOLD)) { + for (k = i; k > 0; k--) { + if ((tbl[k].dbc > tbl[j].dbc) || + (tbl[j].dbc - tbl[k].dbc > + DBC_THRESHOLD)) { + SWAP(tbl, j, k); + } + break; + } + } + break; + } + i = j; + } while (i < len); +} + static void out_stream_callback(struct fw_iso_context *context, u32 cycle, size_t header_length, void *header, void *private_data) @@ -761,30 +810,65 @@ static void in_stream_callback(struct fw_iso_context *context, u32 cycle, void *private_data) { struct amdtp_stream *s = private_data; - unsigned int p, packets, syt, data_quadlets; + struct sort_table *entry, *tbl = s->sort_table; + unsigned int i, j, k, index, packets, syt, remain_packets; __be32 *buffer, *headers = header; /* The number of packets in buffer */ packets = header_length / IN_PACKET_HEADER_SIZE; - for (p = 0; p < packets; p++) { - buffer = s->buffer.packets[s->packet_index].buffer; + /* Store into sort table and sort. */ + for (i = 0; i < packets; i++) { + entry = &tbl[s->remain_packets + i]; + entry->id = i; - /* The number of quadlets in this packet */ - data_quadlets = - (be32_to_cpu(headers[p]) >> ISO_DATA_LENGTH_SHIFT) / 4; + index = s->packet_index + i; + if (index >= QUEUE_LENGTH) + index -= QUEUE_LENGTH; + buffer = s->buffer.packets[index].buffer; + entry->dbc = be32_to_cpu(buffer[0]) & AMDTP_DBC_MASK; - /* Process sync slave stream */ - if ((s->flags & CIP_BLOCKING) && - (s->flags & CIP_SYNC_TO_DEVICE) && - s->sync_slave->callbacked) { - syt = be32_to_cpu(buffer[1]) & CIP_SYT_MASK; - handle_out_packet(s->sync_slave, syt); - } + entry->payload_size = be32_to_cpu(headers[i]) >> + ISO_DATA_LENGTH_SHIFT; + } + packet_sort(tbl, packets + s->remain_packets); - /* handle each packet data */ - handle_in_packet(s, data_quadlets, buffer); + /* + * for convinience, tbl[i].id >= QUEUE_LENGTH is a label to identify + * previous packets in buffer. + */ + remain_packets = s->remain_packets; + s->remain_packets = packets / 4; + for (i = 0, j = 0, k = 0; i < remain_packets + packets; i++) { + if (tbl[i].id < QUEUE_LENGTH) { + index = s->packet_index + tbl[i].id; + if (index >= QUEUE_LENGTH) + index -= QUEUE_LENGTH; + buffer = s->buffer.packets[index].buffer; + } else + buffer = s->left_packets + + amdtp_stream_get_max_payload(s) * j++; + + if (i < remain_packets + packets - s->remain_packets) { + /* Process sync slave stream */ + if ((s->flags & CIP_BLOCKING) && + (s->flags & CIP_SYNC_TO_DEVICE) && + s->sync_slave->callbacked) { + syt = be32_to_cpu(buffer[1]) & CIP_SYT_MASK; + handle_out_packet(s->sync_slave, syt); + } + handle_in_packet(s, tbl[i].payload_size / 4, buffer); + } else { + tbl[k].id = tbl[i].id + QUEUE_LENGTH; + tbl[k].dbc = tbl[i].dbc; + tbl[k].payload_size = tbl[i].payload_size; + memcpy(s->left_packets + + amdtp_stream_get_max_payload(s) * k++, + buffer, tbl[i].payload_size); + } + } + for (i = 0; i < packets; i++) { if (queue_in_packet(s) < 0) { amdtp_stream_pcm_abort(s); return; @@ -888,6 +972,17 @@ int amdtp_stream_start(struct amdtp_stream *s, int channel, int speed) if (err < 0) goto err_unlock; + /* for sorting transmitted packets */ + if (s->direction == AMDTP_IN_STREAM) { + s->remain_packets = 0; + s->sort_table = kzalloc(sizeof(struct sort_table) * + QUEUE_LENGTH, GFP_KERNEL); + if (s->sort_table == NULL) + return -ENOMEM; + s->left_packets = kzalloc(amdtp_stream_get_max_payload(s) * + QUEUE_LENGTH / 4, GFP_KERNEL); + } + s->context = fw_iso_context_create(fw_parent_device(s->unit)->card, type, channel, speed, header_size, amdtp_stream_callback, s); @@ -986,6 +1081,11 @@ void amdtp_stream_stop(struct amdtp_stream *s) s->context = ERR_PTR(-1); iso_packets_buffer_destroy(&s->buffer, s->unit); + if (s->sort_table != NULL) + kfree(s->sort_table); + if (s->left_packets != NULL) + kfree(s->left_packets); + s->callbacked = false; mutex_unlock(&s->mutex); diff --git a/sound/firewire/amdtp.h b/sound/firewire/amdtp.h index 0e73313..4cf7a7e 100644 --- a/sound/firewire/amdtp.h +++ b/sound/firewire/amdtp.h @@ -109,6 +109,10 @@ struct amdtp_stream { bool callbacked; wait_queue_head_t callback_wait; struct amdtp_stream *sync_slave; + + void *sort_table; + void *left_packets; + unsigned int remain_packets; }; int amdtp_stream_init(struct amdtp_stream *s, struct fw_unit *unit, -- 1.8.3.2 |
From: Takashi S. <o-t...@sa...> - 2014-01-29 13:45:32
|
For duplex streams with synchronization, drivers should pick up 'presentation timestamp' from in-packets and use the timestamp for out-packets. This commit is preparation for this. Signed-off-by: Takashi Sakamoto <o-t...@sa...> --- sound/firewire/amdtp.c | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/sound/firewire/amdtp.c b/sound/firewire/amdtp.c index ad3521f..56b01c8 100644 --- a/sound/firewire/amdtp.c +++ b/sound/firewire/amdtp.c @@ -256,7 +256,9 @@ static unsigned int calculate_data_blocks(struct amdtp_stream *s) { unsigned int phase, data_blocks; - if (!cip_sfc_is_base_44100(s->sfc)) { + if (s->flags & CIP_BLOCKING) + data_blocks = s->syt_interval; + else if (!cip_sfc_is_base_44100(s->sfc)) { /* Sample_rate / 8000 is an integer, and precomputed. */ data_blocks = s->data_block_state; } else { @@ -620,26 +622,22 @@ static inline int queue_in_packet(struct amdtp_stream *s) amdtp_stream_get_max_payload(s), false); } -static void handle_out_packet(struct amdtp_stream *s, unsigned int cycle) +static void handle_out_packet(struct amdtp_stream *s, unsigned int syt) { __be32 *buffer; - unsigned int index, data_blocks, syt, payload_length; + unsigned int data_blocks, payload_length; struct snd_pcm_substream *pcm; if (s->packet_index < 0) return; - index = s->packet_index; /* this module generate empty packet for 'no data' */ - syt = calculate_syt(s, cycle); - if (!(s->flags & CIP_BLOCKING)) + if (!(s->flags & CIP_BLOCKING) || (syt != CIP_SYT_NO_INFO)) data_blocks = calculate_data_blocks(s); - else if (syt != CIP_SYT_NO_INFO) - data_blocks = s->syt_interval; else data_blocks = 0; - buffer = s->buffer.packets[index].buffer; + buffer = s->buffer.packets[s->packet_index].buffer; buffer[0] = cpu_to_be32(ACCESS_ONCE(s->source_node_id_field) | (s->data_block_quadlets << AMDTP_DBS_SHIFT) | s->data_block_counter); @@ -737,7 +735,7 @@ static void out_stream_callback(struct fw_iso_context *context, u32 cycle, void *private_data) { struct amdtp_stream *s = private_data; - unsigned int i, packets = header_length / 4; + unsigned int i, syt, packets = header_length / 4; /* * Compute the cycle of the last queued packet. @@ -746,8 +744,10 @@ static void out_stream_callback(struct fw_iso_context *context, u32 cycle, */ cycle += QUEUE_LENGTH - packets; - for (i = 0; i < packets; ++i) - handle_out_packet(s, ++cycle); + for (i = 0; i < packets; ++i) { + syt = calculate_syt(s, ++cycle); + handle_out_packet(s, syt); + } fw_iso_context_queue_flush(s->context); } -- 1.8.3.2 |
From: Takashi S. <o-t...@sa...> - 2014-01-29 13:45:36
|
This patch adds some macros, codes with condition of direction and new functions to handle output connection. Once cmp_connection_init() is executed with its direction, CMP input and output connection can be handled by the same way. Signed-off-by: Takashi Sakamoto <o-t...@sa...> --- sound/firewire/cmp.c | 113 +++++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 104 insertions(+), 9 deletions(-) diff --git a/sound/firewire/cmp.c b/sound/firewire/cmp.c index 1430fec..00b1b2d 100644 --- a/sound/firewire/cmp.c +++ b/sound/firewire/cmp.c @@ -29,6 +29,16 @@ #define PCR_CHANNEL_MASK 0x003f0000 #define PCR_CHANNEL_SHIFT 16 +/* oPCR specific fields */ +#define OPCR_XSPEED_MASK 0x00C00000 +#define OPCR_XSPEED_SHIFT 22 +#define OPCR_SPEED_MASK 0x0000C000 +#define OPCR_SPEED_SHIFT 14 +#define OPCR_OVERHEAD_ID_MASK 0x00003C00 +#define OPCR_OVERHEAD_ID_SHIFT 10 +#define OPCR_PAYLOAD_MASK 0x000003FF +#define OPCR_PAYLOAD_SHIFT 0 + enum bus_reset_handling { ABORT_ON_BUS_RESET, SUCCEED_ON_BUS_RESET, @@ -41,10 +51,30 @@ void cmp_error(struct cmp_connection *c, const char *fmt, ...) va_start(va, fmt); dev_err(&c->resources.unit->device, "%cPCR%u: %pV", - 'i', c->pcr_index, &(struct va_format){ fmt, &va }); + (c->direction == CMP_INPUT) ? 'i' : 'o', + c->pcr_index, &(struct va_format){ fmt, &va }); va_end(va); } +static unsigned long long get_offset(struct cmp_connection *c, bool master) +{ + unsigned long long offset = CSR_REGISTER_BASE; + + if (!master) { + if (c->direction == CMP_INPUT) + offset += CSR_IPCR(c->pcr_index); + else + offset += CSR_OPCR(c->pcr_index); + } else { + if (c->direction == CMP_INPUT) + offset += CSR_IMPR; + else + offset += CSR_OMPR; + } + + return offset; +} + static int pcr_modify(struct cmp_connection *c, __be32 (*modify)(struct cmp_connection *c, __be32 old), int (*check)(struct cmp_connection *c, __be32 pcr), @@ -60,8 +90,7 @@ static int pcr_modify(struct cmp_connection *c, err = snd_fw_transaction( c->resources.unit, TCODE_LOCK_COMPARE_SWAP, - CSR_REGISTER_BASE + CSR_IPCR(c->pcr_index), - buffer, 8, + get_offset(c, false), buffer, 8, FW_FIXED_GENERATION | c->resources.generation); if (err < 0) { @@ -102,8 +131,7 @@ int cmp_connection_init(struct cmp_connection *c, int err; err = snd_fw_transaction(unit, TCODE_READ_QUADLET_REQUEST, - CSR_REGISTER_BASE + CSR_IMPR, - &mpr_be, 4, 0); + get_offset(c, true), &mpr_be, 4, 0); if (err < 0) return err; mpr = be32_to_cpu(mpr_be); @@ -122,6 +150,7 @@ int cmp_connection_init(struct cmp_connection *c, c->max_speed = (mpr & MPR_SPEED_MASK) >> MPR_SPEED_SHIFT; if (c->max_speed == SCODE_BETA) c->max_speed += (mpr & MPR_XSPEED_MASK) >> MPR_XSPEED_SHIFT; + c->direction = direction; return 0; } @@ -151,6 +180,62 @@ static __be32 ipcr_set_modify(struct cmp_connection *c, __be32 ipcr) return ipcr; } +static int get_overhead_id(struct cmp_connection *c) +{ + int id; + + /* + * apply "oPCR overhead ID encoding" + * the encoding table can convert up to 512. + * here the value over 512 is converted as the same way as 512. + */ + for (id = 1; id < 16; id++) { + if (c->resources.bandwidth_overhead < (id << 5)) + break; + } + if (id == 16) + id = 0; + + return id; +} + +static __be32 opcr_set_modify(struct cmp_connection *c, __be32 opcr) +{ + unsigned int spd, xspd; + + /* generate speed and extended speed field value */ + if (c->speed > SCODE_400) { + spd = SCODE_800; + xspd = c->speed - SCODE_800; + } else { + spd = c->speed; + xspd = 0; + } + + opcr &= ~cpu_to_be32(PCR_BCAST_CONN | + PCR_P2P_CONN_MASK | + OPCR_XSPEED_MASK | + PCR_CHANNEL_MASK | + OPCR_SPEED_MASK | + OPCR_OVERHEAD_ID_MASK | + OPCR_PAYLOAD_MASK); + opcr |= cpu_to_be32(1 << PCR_P2P_CONN_SHIFT); + opcr |= cpu_to_be32(xspd << OPCR_XSPEED_SHIFT); + opcr |= cpu_to_be32(c->resources.channel << PCR_CHANNEL_SHIFT); + opcr |= cpu_to_be32(spd << OPCR_SPEED_SHIFT); + opcr |= cpu_to_be32(get_overhead_id(c) << OPCR_OVERHEAD_ID_SHIFT); + /* + * here zero is applied to payload field. + * it means the maximum number of quadlets in an isochronous packet is + * 1024 when spd is less than three, 1024 * 2 * xspd + 1 when spd is + * equal to three. An arbitrary value can be set here but 0 is enough + * for our purpose. + */ + opcr |= cpu_to_be32(0 << OPCR_PAYLOAD_SHIFT); + + return opcr; +} + static int pcr_set_check(struct cmp_connection *c, __be32 pcr) { if (pcr & cpu_to_be32(PCR_BCAST_CONN | @@ -196,8 +281,13 @@ retry_after_bus_reset: if (err < 0) goto err_mutex; - err = pcr_modify(c, ipcr_set_modify, pcr_set_check, - ABORT_ON_BUS_RESET); + if (c->direction == CMP_OUTPUT) + err = pcr_modify(c, opcr_set_modify, pcr_set_check, + ABORT_ON_BUS_RESET); + else + err = pcr_modify(c, ipcr_set_modify, pcr_set_check, + ABORT_ON_BUS_RESET); + if (err == -EAGAIN) { fw_iso_resources_free(&c->resources); goto retry_after_bus_reset; @@ -245,8 +335,13 @@ int cmp_connection_update(struct cmp_connection *c) if (err < 0) goto err_unconnect; - err = pcr_modify(c, ipcr_set_modify, pcr_set_check, - SUCCEED_ON_BUS_RESET); + if (c->direction == CMP_OUTPUT) + err = pcr_modify(c, opcr_set_modify, pcr_set_check, + SUCCEED_ON_BUS_RESET); + else + err = pcr_modify(c, ipcr_set_modify, pcr_set_check, + SUCCEED_ON_BUS_RESET); + if (err < 0) goto err_resources; -- 1.8.3.2 |
From: Takashi S. <o-t...@sa...> - 2014-01-29 13:45:37
|
Some devices arrange the position of PCM/MIDI data channel in AMDTP packet. This commit allows drivers to set channel mapping. To be simple, the mapping table is an array with fixed length. Then the number of channels for PCM is restricted by 64 channels. Signed-off-by: Takashi Sakamoto <o-t...@sa...> --- sound/firewire/amdtp.c | 122 +++++++++++++++++++++++++++++-------------------- sound/firewire/amdtp.h | 8 ++++ 2 files changed, 80 insertions(+), 50 deletions(-) diff --git a/sound/firewire/amdtp.c b/sound/firewire/amdtp.c index 881d610..73f36fb 100644 --- a/sound/firewire/amdtp.c +++ b/sound/firewire/amdtp.c @@ -140,11 +140,12 @@ void amdtp_stream_set_parameters(struct amdtp_stream *s, [CIP_SFC_176400] = 176400, [CIP_SFC_192000] = 192000, }; - unsigned int sfc, midi_channels; + unsigned int i, sfc, midi_channels; midi_channels = DIV_ROUND_UP(midi_ports, 8); - if (WARN_ON(amdtp_stream_running(s)) || + if (WARN_ON(amdtp_stream_running(s)) | + WARN_ON(pcm_channels > AMDTP_MAX_CHANNELS_FOR_PCM) | WARN_ON(midi_channels > AMDTP_MAX_CHANNELS_FOR_MIDI)) return; @@ -159,11 +160,12 @@ sfc_found: if (s->dual_wire) { sfc -= 2; rate /= 2; - pcm_channels *= 2; + s->pcm_channels = pcm_channels * 2; + } else { + s->pcm_channels = pcm_channels; } s->sfc = sfc; - s->data_block_quadlets = pcm_channels + midi_channels; - s->pcm_channels = pcm_channels; + s->data_block_quadlets = s->pcm_channels + midi_channels; s->midi_ports = midi_ports; s->syt_interval = amdtp_syt_intervals[sfc]; @@ -173,6 +175,11 @@ sfc_found: if (s->flags & CIP_BLOCKING) /* additional buffering needed to adjust for no-data packets */ s->transfer_delay += TICKS_PER_SECOND * s->syt_interval / rate; + + /* init the position map for PCM and MIDI channels */ + for (i = 0; i < pcm_channels; i++) + s->pcm_positions[i] = i; + s->midi_position = s->pcm_channels; } EXPORT_SYMBOL(amdtp_stream_set_parameters); @@ -351,22 +358,20 @@ static void amdtp_write_s32(struct amdtp_stream *s, __be32 *buffer, unsigned int frames) { struct snd_pcm_runtime *runtime = pcm->runtime; - unsigned int channels, remaining_frames, frame_step, i, c; + unsigned int remaining_frames, i, c; const u32 *src; - channels = s->pcm_channels; src = (void *)runtime->dma_area + frames_to_bytes(runtime, s->pcm_buffer_pointer); remaining_frames = runtime->buffer_size - s->pcm_buffer_pointer; - frame_step = s->data_block_quadlets - channels; for (i = 0; i < frames; ++i) { - for (c = 0; c < channels; ++c) { - *buffer = cpu_to_be32((*src >> 8) | 0x40000000); + for (c = 0; c < s->pcm_channels; ++c) { + buffer[s->pcm_positions[c]] = + cpu_to_be32((*src >> 8) | 0x40000000); src++; - buffer++; } - buffer += frame_step; + buffer += s->data_block_quadlets; if (--remaining_frames == 0) src = (void *)runtime->dma_area; } @@ -377,22 +382,20 @@ static void amdtp_write_s16(struct amdtp_stream *s, __be32 *buffer, unsigned int frames) { struct snd_pcm_runtime *runtime = pcm->runtime; - unsigned int channels, remaining_frames, frame_step, i, c; + unsigned int remaining_frames, i, c; const u16 *src; - channels = s->pcm_channels; src = (void *)runtime->dma_area + frames_to_bytes(runtime, s->pcm_buffer_pointer); remaining_frames = runtime->buffer_size - s->pcm_buffer_pointer; - frame_step = s->data_block_quadlets - channels; for (i = 0; i < frames; ++i) { - for (c = 0; c < channels; ++c) { - *buffer = cpu_to_be32((*src << 8) | 0x40000000); + for (c = 0; c < s->pcm_channels; ++c) { + buffer[s->pcm_positions[c]] = + cpu_to_be32((*src << 8) | 0x40000000); src++; - buffer++; } - buffer += frame_step; + buffer += s->data_block_quadlets; if (--remaining_frames == 0) src = (void *)runtime->dma_area; } @@ -403,29 +406,29 @@ static void amdtp_write_s32_dualwire(struct amdtp_stream *s, __be32 *buffer, unsigned int frames) { struct snd_pcm_runtime *runtime = pcm->runtime; - unsigned int channels, frame_adjust_1, frame_adjust_2, i, c; + unsigned int channels, remaining_frames, i, c; const u32 *src; - channels = s->pcm_channels; src = (void *)runtime->dma_area + - s->pcm_buffer_pointer * (runtime->frame_bits / 8); - frame_adjust_1 = channels - 1; - frame_adjust_2 = 1 - (s->data_block_quadlets - channels); + frames_to_bytes(runtime, s->pcm_buffer_pointer); + remaining_frames = runtime->buffer_size - s->pcm_buffer_pointer; + channels = s->pcm_channels / 2; - channels /= 2; for (i = 0; i < frames; ++i) { for (c = 0; c < channels; ++c) { - *buffer = cpu_to_be32((*src >> 8) | 0x40000000); + buffer[s->pcm_positions[c] * 2] = + cpu_to_be32((*src >> 8) | 0x40000000); src++; - buffer += 2; } - buffer -= frame_adjust_1; + buffer += 1; for (c = 0; c < channels; ++c) { - *buffer = cpu_to_be32((*src >> 8) | 0x40000000); + buffer[s->pcm_positions[c] * 2] = + cpu_to_be32((*src >> 8) | 0x40000000); src++; - buffer += 2; } - buffer -= frame_adjust_2; + buffer += s->data_block_quadlets - 1; + if (--remaining_frames == 0) + src = (void *)runtime->dma_area; } } @@ -434,29 +437,29 @@ static void amdtp_write_s16_dualwire(struct amdtp_stream *s, __be32 *buffer, unsigned int frames) { struct snd_pcm_runtime *runtime = pcm->runtime; - unsigned int channels, frame_adjust_1, frame_adjust_2, i, c; + unsigned int channels, remaining_frames, i, c; const u16 *src; - channels = s->pcm_channels; src = (void *)runtime->dma_area + - s->pcm_buffer_pointer * (runtime->frame_bits / 8); - frame_adjust_1 = channels - 1; - frame_adjust_2 = 1 - (s->data_block_quadlets - channels); + frames_to_bytes(runtime, s->pcm_buffer_pointer); + remaining_frames = runtime->buffer_size - s->pcm_buffer_pointer; + channels = s->pcm_channels / 2; - channels /= 2; for (i = 0; i < frames; ++i) { for (c = 0; c < channels; ++c) { - *buffer = cpu_to_be32((*src << 8) | 0x40000000); + buffer[s->pcm_positions[c] * 2] = + cpu_to_be32((*src << 8) | 0x40000000); src++; - buffer += 2; } - buffer -= frame_adjust_1; + buffer += 1; for (c = 0; c < channels; ++c) { - *buffer = cpu_to_be32((*src << 8) | 0x40000000); + buffer[s->pcm_positions[c] * 2] = + cpu_to_be32((*src << 8) | 0x40000000); src++; - buffer += 2; } - buffer -= frame_adjust_2; + buffer += s->data_block_quadlets - 1; + if (--remaining_frames == 0) + src = (void *)runtime->dma_area; } } @@ -474,7 +477,7 @@ static void amdtp_read_s32(struct amdtp_stream *s, for (i = 0; i < frames; ++i) { for (c = 0; c < s->pcm_channels; ++c) { - *dst = be32_to_cpu(buffer[c]) << 8; + *dst = be32_to_cpu(buffer[s->pcm_positions[c]]) << 8; dst++; } buffer += s->data_block_quadlets; @@ -498,12 +501,14 @@ static void amdtp_read_s32_dualwire(struct amdtp_stream *s, for (i = 0; i < frames; ++i) { for (c = 0; c < channels; ++c) { - *dst = be32_to_cpu(buffer[c * 2]) << 8; + *dst = + be32_to_cpu(buffer[s->pcm_positions[c] * 2]) << 8; dst++; } buffer += 1; for (c = 0; c < channels; ++c) { - *dst = be32_to_cpu(buffer[c * 2]) << 8; + *dst = + be32_to_cpu(buffer[s->pcm_positions[c] * 2]) << 8; dst++; } buffer += s->data_block_quadlets - 1; @@ -519,11 +524,26 @@ static void amdtp_fill_pcm_silence(struct amdtp_stream *s, for (i = 0; i < frames; ++i) { for (c = 0; c < s->pcm_channels; ++c) - buffer[c] = cpu_to_be32(0x40000000); + buffer[s->pcm_positions[c]] = cpu_to_be32(0x40000000); buffer += s->data_block_quadlets; } } +static void amdtp_fill_pcm_silence_dualwire(struct amdtp_stream *s, + __be32 *buffer, unsigned int frames) +{ + unsigned int i, c, channels; + + channels = s->pcm_channels / 2; + for (i = 0; i < frames; ++i) { + for (c = 0; c < channels; ++c) { + buffer[s->pcm_positions[c] * 2] = + buffer[s->pcm_positions[c] * 2 + 1] = + cpu_to_be32(0x40000000); + } + buffer += s->data_block_quadlets; + } +} static void amdtp_fill_midi(struct amdtp_stream *s, __be32 *buffer, unsigned int frames) { @@ -531,8 +551,8 @@ static void amdtp_fill_midi(struct amdtp_stream *s, u8 *b; for (f = 0; f < frames; f++) { - buffer[s->pcm_channels + 1] = 0x00; - b = (u8 *)&buffer[s->pcm_channels + 1]; + buffer[s->midi_position] = 0x00; + b = (u8 *)&buffer[s->midi_position]; port = (s->data_block_counter + f) % 8; if ((s->midi[port] == NULL) || @@ -555,7 +575,7 @@ static void amdtp_pull_midi(struct amdtp_stream *s, for (f = 0; f < frames; f++) { port = (s->data_block_counter + f) % 8; - b = (u8 *)&buffer[s->pcm_channels + 1]; + b = (u8 *)&buffer[s->midi_position]; len = b[0] - 0x80; if (len < 1 || 3 < len) @@ -664,6 +684,8 @@ static void handle_out_packet(struct amdtp_stream *s, unsigned int syt) pcm = ACCESS_ONCE(s->pcm); if (pcm) s->transfer_samples(s, pcm, buffer, data_blocks); + else if (s->dual_wire) + amdtp_fill_pcm_silence_dualwire(s, buffer, data_blocks); else amdtp_fill_pcm_silence(s, buffer, data_blocks); if (s->midi_ports) diff --git a/sound/firewire/amdtp.h b/sound/firewire/amdtp.h index 4cf7a7e..d7bd81a 100644 --- a/sound/firewire/amdtp.h +++ b/sound/firewire/amdtp.h @@ -49,6 +49,12 @@ enum cip_sfc { /* + * This module supports maximum 64 PCM channels for one PCM stream + * This is for our convinience. + */ +#define AMDTP_MAX_CHANNELS_FOR_PCM 64 + +/* * AMDTP packet can include channels for MIDI conformant data. * Each MIDI conformant data channel includes 8 MPX-MIDI data stream. * Each MPX-MIDI data stream includes one data stream from/to MIDI ports. @@ -83,6 +89,8 @@ struct amdtp_stream { void (*transfer_samples)(struct amdtp_stream *s, struct snd_pcm_substream *pcm, __be32 *buffer, unsigned int frames); + u8 pcm_positions[AMDTP_MAX_CHANNELS_FOR_PCM]; + u8 midi_position; unsigned int syt_interval; unsigned int transfer_delay; -- 1.8.3.2 |
From: Takashi S. <o-t...@sa...> - 2014-01-29 13:45:40
|
Plug Control Registers have two fields related to the number of established connections, one is 'Broadcast connection counter' and another is 'Point-to-point connection counter'. The driver can know there are established connections or not to check these fields. This is considering for JACK/FFADO streaming. Currently, when JACK/FFADO starts its streaming to the device, cmp_connection_establish() is failed expectedly. This seems to be enough but there are some devices which needs to change samplling frequency before trying to establish connections. For such devices, this functionality is needed. Signed-off-by: Takashi Sakamoto <o-t...@sa...> --- sound/firewire/cmp.c | 18 ++++++++++++++++++ sound/firewire/cmp.h | 1 + 2 files changed, 19 insertions(+) diff --git a/sound/firewire/cmp.c b/sound/firewire/cmp.c index 00b1b2d..354920a 100644 --- a/sound/firewire/cmp.c +++ b/sound/firewire/cmp.c @@ -157,6 +157,24 @@ int cmp_connection_init(struct cmp_connection *c, EXPORT_SYMBOL(cmp_connection_init); /** + * cmp_connection_check_used - check connection is already esablished or not + * @c: the connection manager to be checked + */ +int cmp_connection_check_used(struct cmp_connection *c, bool *used) +{ + __be32 pcr; + int err; + + err = snd_fw_transaction( + c->resources.unit, TCODE_READ_QUADLET_REQUEST, + get_offset(c, false), &pcr, 4, 0); + if (err >= 0) + *used = (pcr & cpu_to_be32(PCR_BCAST_CONN | PCR_P2P_CONN_MASK)); + return err; +} +EXPORT_SYMBOL(cmp_connection_check_used); + +/** * cmp_connection_destroy - free connection manager resources * @c: the connection manager */ diff --git a/sound/firewire/cmp.h b/sound/firewire/cmp.h index 9b58448..ebcb484 100644 --- a/sound/firewire/cmp.h +++ b/sound/firewire/cmp.h @@ -38,6 +38,7 @@ int cmp_connection_init(struct cmp_connection *connection, struct fw_unit *unit, enum cmp_direction direction, unsigned int pcr_index); +int cmp_connection_check_used(struct cmp_connection *connection, bool *used); void cmp_connection_destroy(struct cmp_connection *connection); int cmp_connection_establish(struct cmp_connection *connection, -- 1.8.3.2 |
From: Takashi S. <o-t...@sa...> - 2014-01-29 13:45:40
|
This commit adds three modifications for Echo Audio's Fireworks quirks. 1.Making this module to support both TAG0 and TAG1 In IEC 61883-1, each common isochronous packet (CIP) has 'tag' field in its packet header. The value is generally 0x01 (CIP header is included = TAG1). But Fireworks in 'IEC 61883 compliant' mode transmits 'no data' packet with 0x00 (No CIP header is included = TAG0) but the packet includes CIP structure. To process 'presentation timestamp' for duplex streams synchronization correctly, this packets needs to be handled. 2.Making this module to calculate data block size and not to check continuity One of Fireworks device, AudioFirePre8, always reports 8 data block size in in-packets and the data block counter is incremented by 8. But actual value is different. 3.Making this module to have the numebr of first data blocks for MIDI messages in out-packet Fireworks can pick up MIDI messages in first 8 data blocks and ignore in other data blocks. I confirm these quirks in firmware version between 4.8 and 5.8 with Windows. I didn't investigate for the other versions. But according to contact person in Echo Audio, there is no differences between firmwares installed by Windows or OS X. Signed-off-by: Takashi Sakamoto <o-t...@sa...> --- sound/firewire/amdtp.c | 50 +++++++++++++++++++++++++------------------------- sound/firewire/amdtp.h | 2 ++ 2 files changed, 27 insertions(+), 25 deletions(-) diff --git a/sound/firewire/amdtp.c b/sound/firewire/amdtp.c index 5b0df21..9b637af 100644 --- a/sound/firewire/amdtp.c +++ b/sound/firewire/amdtp.c @@ -88,6 +88,8 @@ int amdtp_stream_init(struct amdtp_stream *s, struct fw_unit *unit, s->sort_table = NULL; s->left_packets = NULL; + s->blocks_for_midi = UINT_MAX; + return 0; } EXPORT_SYMBOL(amdtp_stream_init); @@ -556,8 +558,14 @@ static void amdtp_fill_midi(struct amdtp_stream *s, buffer[s->midi_position] = 0x00; b = (u8 *)&buffer[s->midi_position]; + /* + * NOTE: + * Fireworks ignores midi messages in more than first 8 + * data blocks of an packet. + */ port = (s->data_block_counter + f) % 8; - if ((s->midi[port] == NULL) || + if ((f >= s->blocks_for_midi) || + (s->midi[port] == NULL) || (snd_rawmidi_transmit(s->midi[port], b + 1, 1) <= 0)) { b[0] = 0x80; b[1] = 0x00; /* confirm to be zero */ @@ -710,7 +718,7 @@ static void handle_in_packet(struct amdtp_stream *s, __be32 *buffer) { u32 cip_header[2]; - unsigned int data_block_quadlets, data_blocks, data_block_counter; + unsigned int data_blocks; struct snd_pcm_substream *pcm; cip_header[0] = be32_to_cpu(buffer[0]); @@ -735,27 +743,16 @@ static void handle_in_packet(struct amdtp_stream *s, AMDTP_FDF_NO_DATA)) return; - data_block_quadlets = - (cip_header[1] & AMDTP_DBS_MASK) >> AMDTP_DBS_SHIFT; - /* avoid division by zero */ - if (data_block_quadlets == 0) { - dev_info_ratelimited(&s->unit->device, - "Detect invalid value in dbs field: %08X\n", - data_block_quadlets); - return; - } - - data_blocks = (payload_quadlets - 2) / data_block_quadlets; - data_block_counter = cip_header[1] & AMDTP_DBC_MASK; - - /* check packet continuity */ - s->data_block_counter = (s->data_block_counter + data_blocks) & 0xff; - if (s->data_block_counter != data_block_counter) { - dev_info_ratelimited(&s->unit->device, - "Detect uncontinuity of CIP packets\n"); - s->data_block_counter = data_block_counter; - return; - } + /* + * This module don't use the value of dbs and dbc beceause Echo + * AudioFirePre8 reports inappropriate value. + * + * This model always reports a fixed value "8" as data block size at + * any sampling rates but actually data block size is different. + * Additionally the value of data block count always incremented by + * "8" at any sampling rates but actually it's different. + */ + data_blocks = (payload_quadlets - 2) / s->data_block_quadlets; buffer += 2; @@ -1040,11 +1037,14 @@ int amdtp_stream_start(struct amdtp_stream *s, int channel, int speed) goto err_context; } while (s->packet_index > 0); - /* NOTE: TAG1 matches CIP. This just affects in stream */ + /* + * NOTE: TAG1 matches CIP. This just affects in stream. + * Fireworks transmits NODATA packets with TAG0. + */ s->data_block_counter = 0; s->callbacked = false; err = fw_iso_context_start(s->context, -1, 0, - FW_ISO_CONTEXT_MATCH_TAG1); + FW_ISO_CONTEXT_MATCH_TAG0 | FW_ISO_CONTEXT_MATCH_TAG1); if (err < 0) goto err_context; diff --git a/sound/firewire/amdtp.h b/sound/firewire/amdtp.h index 7da1cbf..d94e62a 100644 --- a/sound/firewire/amdtp.h +++ b/sound/firewire/amdtp.h @@ -113,6 +113,8 @@ struct amdtp_stream { bool pointer_flush; struct snd_rawmidi_substream *midi[AMDTP_MAX_CHANNELS_FOR_MIDI * 8]; + /* quirk: the first count of data blocks in an AMDTP packet for MIDI */ + unsigned int blocks_for_midi; bool callbacked; wait_queue_head_t callback_wait; -- 1.8.3.2 |
From: Takashi S. <o-t...@sa...> - 2014-01-29 13:45:39
|
This patch adds 'direction' member to 'cmp_connection' structure to indicate the direction of connection. This patch also adds 'direction' argument to cmp_connection_init() function to determine the direction. The cmp_connection_init() function is exported and used in snd-firewire-speakers so this patch also affect it. This patch just add them. Actual implementation will be done by followed patches. Signed-off-by: Takashi Sakamoto <o-t...@sa...> --- sound/firewire/cmp.c | 1 + sound/firewire/cmp.h | 7 +++++++ sound/firewire/speakers.c | 2 +- 3 files changed, 9 insertions(+), 1 deletion(-) diff --git a/sound/firewire/cmp.c b/sound/firewire/cmp.c index 17ad4f2..1430fec 100644 --- a/sound/firewire/cmp.c +++ b/sound/firewire/cmp.c @@ -94,6 +94,7 @@ static int pcr_modify(struct cmp_connection *c, */ int cmp_connection_init(struct cmp_connection *c, struct fw_unit *unit, + enum cmp_direction direction, unsigned int pcr_index) { __be32 mpr_be; diff --git a/sound/firewire/cmp.h b/sound/firewire/cmp.h index 2320cd4..9b58448 100644 --- a/sound/firewire/cmp.h +++ b/sound/firewire/cmp.h @@ -7,6 +7,11 @@ struct fw_unit; +enum cmp_direction { + CMP_INPUT = 0, + CMP_OUTPUT, +}; + /** * struct cmp_connection - manages an isochronous connection to a device * @speed: the connection's actual speed @@ -26,10 +31,12 @@ struct cmp_connection { __be32 last_pcr_value; unsigned int pcr_index; unsigned int max_speed; + enum cmp_direction direction; }; int cmp_connection_init(struct cmp_connection *connection, struct fw_unit *unit, + enum cmp_direction direction, unsigned int pcr_index); void cmp_connection_destroy(struct cmp_connection *connection); diff --git a/sound/firewire/speakers.c b/sound/firewire/speakers.c index f5d0bf3..350e578 100644 --- a/sound/firewire/speakers.c +++ b/sound/firewire/speakers.c @@ -679,7 +679,7 @@ static int fwspk_probe(struct fw_unit *unit, fwspk->unit = fw_unit_get(unit); fwspk->device_info = (const struct device_info *)id->driver_data; - err = cmp_connection_init(&fwspk->connection, unit, 0); + err = cmp_connection_init(&fwspk->connection, unit, CMP_INPUT, 0); if (err < 0) goto err_unit; -- 1.8.3.2 |
From: Takashi S. <o-t...@sa...> - 2014-01-29 13:45:43
|
This commit adds a new driver with no functionality. This driver just creates/removes card instance according to callbacks. Fireworks is a board module which Echo Audio produced. This module consists of three chipsets: - Communication chipset for IEEE1394 PHY/Link and IEC 61883-1/6 - DSP or/and FPGA for signal processing - Flash Memory to store firmwares Current supported devices: - Mackie Onyx 400F/1200F - Echo AudioFire12/8(until 2009 July) - Echo AudioFire2/4/Pre8/8(since 2009 July) - Echo Fireworks 8/HDMI - Gibson Robot Interface pack/GoldTop Signed-off-by: Takashi Sakamoto <o-t...@sa...> --- sound/firewire/Kconfig | 14 +++ sound/firewire/Makefile | 1 + sound/firewire/fireworks/Makefile | 2 + sound/firewire/fireworks/fireworks.c | 184 +++++++++++++++++++++++++++++++++++ sound/firewire/fireworks/fireworks.h | 41 ++++++++ 5 files changed, 242 insertions(+) create mode 100644 sound/firewire/fireworks/Makefile create mode 100644 sound/firewire/fireworks/fireworks.c create mode 100644 sound/firewire/fireworks/fireworks.h diff --git a/sound/firewire/Kconfig b/sound/firewire/Kconfig index b3e274f..8cd4f1f 100644 --- a/sound/firewire/Kconfig +++ b/sound/firewire/Kconfig @@ -61,4 +61,18 @@ config SND_SCS1X To compile this driver as a module, choose M here: the module will be called snd-scs1x. +config SND_FIREWORKS + tristate "Echo Fireworks board module support" + help + Say Y here to include support for FireWire devices based + on Echo Digital Audio Fireworks board: + * Mackie Onyx 400F/1200F + * Echo AudioFire12/8(until 2009 July) + * Echo AudioFire2/4/Pre8/8(since 2009 July) + * Echo Fireworks 8/HDMI + * Gibson Robot Interface Pack/GoldTop + + To compile this driver as a module, choose M here: the module + will be called snd-fireworks. + endif # SND_FIREWIRE diff --git a/sound/firewire/Makefile b/sound/firewire/Makefile index 5099550..5cd39dc 100644 --- a/sound/firewire/Makefile +++ b/sound/firewire/Makefile @@ -10,3 +10,4 @@ obj-$(CONFIG_SND_DICE) += snd-dice.o obj-$(CONFIG_SND_FIREWIRE_SPEAKERS) += snd-firewire-speakers.o obj-$(CONFIG_SND_ISIGHT) += snd-isight.o obj-$(CONFIG_SND_SCS1X) += snd-scs1x.o +obj-$(CONFIG_SND_FIREWORKS) += fireworks/ diff --git a/sound/firewire/fireworks/Makefile b/sound/firewire/fireworks/Makefile new file mode 100644 index 0000000..99f6fc3 --- /dev/null +++ b/sound/firewire/fireworks/Makefile @@ -0,0 +1,2 @@ +snd-fireworks-objs := fireworks.o +obj-m += snd-fireworks.o diff --git a/sound/firewire/fireworks/fireworks.c b/sound/firewire/fireworks/fireworks.c new file mode 100644 index 0000000..33a0acb --- /dev/null +++ b/sound/firewire/fireworks/fireworks.c @@ -0,0 +1,184 @@ +/* + * fireworks.c - a part of driver for Fireworks based devices + * + * Copyright (c) 2009-2010 Clemens Ladisch + * Copyright (c) 2013 Takashi Sakamoto + * + * Licensed under the terms of the GNU General Public License, version 2. + */ + +/* + * Fireworks is a board module which Echo Audio produced. This module consists + * of three chipsets: + * - Communication chipset for IEEE1394 PHY/Link and IEC 61883-1/6 + * - DSP or/and FPGA for signal processing + * - Flash Memory to store firmwares + */ + +#include "fireworks.h" + +MODULE_DESCRIPTION("Echo Fireworks driver"); +MODULE_AUTHOR("Takashi Sakamoto <o-t...@sa...>"); +MODULE_LICENSE("GPL v2"); + +static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; +static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; +static bool enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP; + +module_param_array(index, int, NULL, 0444); +MODULE_PARM_DESC(index, "card index"); +module_param_array(id, charp, NULL, 0444); +MODULE_PARM_DESC(id, "ID string"); +module_param_array(enable, bool, NULL, 0444); +MODULE_PARM_DESC(enable, "enable Fireworks sound card"); + +static DEFINE_MUTEX(devices_mutex); +static unsigned int devices_used; + +#define VENDOR_LOUD 0x000ff2 +#define MODEL_MACKIE_400F 0x00400f +#define MODEL_MACKIE_1200F 0x01200f + +#define VENDOR_ECHO 0x001486 +#define MODEL_ECHO_AUDIOFIRE_12 0x00af12 +#define MODEL_ECHO_AUDIOFIRE_12HD 0x0af12d +#define MODEL_ECHO_AUDIOFIRE_12_APPLE 0x0af12a +/* This is applied for AudioFire8 (until 2009 July) */ +#define MODEL_ECHO_AUDIOFIRE_8 0x000af8 +#define MODEL_ECHO_AUDIOFIRE_2 0x000af2 +#define MODEL_ECHO_AUDIOFIRE_4 0x000af4 +/* AudioFire9 is applied for AudioFire8(since 2009 July) and AudioFirePre8 */ +#define MODEL_ECHO_AUDIOFIRE_9 0x000af9 +/* unknown as product */ +#define MODEL_ECHO_FIREWORKS_8 0x0000f8 +#define MODEL_ECHO_FIREWORKS_HDMI 0x00afd1 + +#define VENDOR_GIBSON 0x00075b +/* for Robot Interface Pack of Dark Fire, Dusk Tiger, Les Paul Standard 2010 */ +#define MODEL_GIBSON_RIP 0x00afb2 +/* unknown as product */ +#define MODEL_GIBSON_GOLDTOP 0x00afb9 + +static void +efw_card_free(struct snd_card *card) +{ + struct snd_efw *efw = card->private_data; + + if (efw->card_index >= 0) { + mutex_lock(&devices_mutex); + devices_used &= ~BIT(efw->card_index); + mutex_unlock(&devices_mutex); + } + + mutex_destroy(&efw->mutex); + + return; +} + +static int +efw_probe(struct fw_unit *unit, + const struct ieee1394_device_id *entry) +{ + struct snd_card *card; + struct snd_efw *efw; + int card_index, err; + + mutex_lock(&devices_mutex); + + /* check registered cards */ + for (card_index = 0; card_index < SNDRV_CARDS; ++card_index) + if (!(devices_used & BIT(card_index)) && enable[card_index]) + break; + if (card_index >= SNDRV_CARDS) { + err = -ENOENT; + goto end; + } + + err = snd_card_create(index[card_index], id[card_index], + THIS_MODULE, sizeof(struct snd_efw), &card); + if (err < 0) + goto end; + card->private_free = efw_card_free; + + efw = card->private_data; + efw->card = card; + efw->unit = unit; + efw->card_index = -1; + mutex_init(&efw->mutex); + spin_lock_init(&efw->lock); + + strcpy(efw->card->driver, "Fireworks"); + strcpy(efw->card->shortname, "Fireworks"); + strcpy(efw->card->longname, "Fireworks"); + strcpy(efw->card->mixername, "Fireworks"); + + snd_card_set_dev(card, &unit->device); + err = snd_card_register(card); + if (err < 0) { + snd_card_free(card); + goto end; + } + dev_set_drvdata(&unit->device, efw); + devices_used |= BIT(card_index); + efw->card_index = card_index; +end: + mutex_unlock(&devices_mutex); + return err; +} + +static void efw_update(struct fw_unit *unit) +{ + return; +} + +static void efw_remove(struct fw_unit *unit) +{ + struct snd_efw *efw = dev_get_drvdata(&unit->device); + snd_card_disconnect(efw->card); + snd_card_free_when_closed(efw->card); + return; +} + +static const struct ieee1394_device_id efw_id_table[] = { + SND_EFW_DEV_ENTRY(VENDOR_LOUD, MODEL_MACKIE_400F), + SND_EFW_DEV_ENTRY(VENDOR_LOUD, MODEL_MACKIE_1200F), + SND_EFW_DEV_ENTRY(VENDOR_ECHO, MODEL_ECHO_AUDIOFIRE_8), + SND_EFW_DEV_ENTRY(VENDOR_ECHO, MODEL_ECHO_AUDIOFIRE_12), + SND_EFW_DEV_ENTRY(VENDOR_ECHO, MODEL_ECHO_AUDIOFIRE_12HD), + SND_EFW_DEV_ENTRY(VENDOR_ECHO, MODEL_ECHO_AUDIOFIRE_12_APPLE), + SND_EFW_DEV_ENTRY(VENDOR_ECHO, MODEL_ECHO_AUDIOFIRE_2), + SND_EFW_DEV_ENTRY(VENDOR_ECHO, MODEL_ECHO_AUDIOFIRE_4), + SND_EFW_DEV_ENTRY(VENDOR_ECHO, MODEL_ECHO_AUDIOFIRE_9), + SND_EFW_DEV_ENTRY(VENDOR_ECHO, MODEL_ECHO_FIREWORKS_8), + SND_EFW_DEV_ENTRY(VENDOR_ECHO, MODEL_ECHO_FIREWORKS_HDMI), + SND_EFW_DEV_ENTRY(VENDOR_GIBSON, MODEL_GIBSON_RIP), + SND_EFW_DEV_ENTRY(VENDOR_GIBSON, MODEL_GIBSON_GOLDTOP), + {} +}; +MODULE_DEVICE_TABLE(ieee1394, efw_id_table); + +static struct fw_driver efw_driver = { + .driver = { + .owner = THIS_MODULE, + .name = "snd-fireworks", + .bus = &fw_bus_type, + }, + .probe = efw_probe, + .update = efw_update, + .remove = efw_remove, + .id_table = efw_id_table, +}; + +static int __init snd_efw_init(void) +{ + return driver_register(&efw_driver.driver); +} + +static void __exit snd_efw_exit(void) +{ + driver_unregister(&efw_driver.driver); + mutex_destroy(&devices_mutex); +} + +module_init(snd_efw_init); +module_exit(snd_efw_exit); diff --git a/sound/firewire/fireworks/fireworks.h b/sound/firewire/fireworks/fireworks.h new file mode 100644 index 0000000..9e29ee4 --- /dev/null +++ b/sound/firewire/fireworks/fireworks.h @@ -0,0 +1,41 @@ +/* + * fireworks.h - a part of driver for Fireworks based devices + * + * Copyright (c) 2009-2010 Clemens Ladisch + * Copyright (c) 2013 Takashi Sakamoto + * + * Licensed under the terms of the GNU General Public License, version 2. + */ +#ifndef SOUND_FIREWORKS_H_INCLUDED +#define SOUND_FIREWORKS_H_INCLUDED + +#include <linux/compat.h> +#include <linux/device.h> +#include <linux/firewire.h> +#include <linux/firewire-constants.h> +#include <linux/module.h> +#include <linux/mod_devicetable.h> +#include <linux/delay.h> +#include <linux/slab.h> + +#include <sound/core.h> +#include <sound/initval.h> + +struct snd_efw { + struct snd_card *card; + struct fw_unit *unit; + int card_index; + + struct mutex mutex; + spinlock_t lock; +}; + +#define SND_EFW_DEV_ENTRY(vendor, model) \ +{ \ + .match_flags = IEEE1394_MATCH_VENDOR_ID | \ + IEEE1394_MATCH_MODEL_ID, \ + .vendor_id = vendor,\ + .model_id = model \ +} + +#endif -- 1.8.3.2 |
From: Takashi S. <o-t...@sa...> - 2014-01-29 13:45:50
|
Fireworks uses own command and response. This commit adds functionality to transact and adds some commands required for sound card instance and kernel streaming. There are two ways to deliver substance of this transaction: 1.AV/C vendor dependent command for command/response 2.Async transaction to specific addresses for command/response By way 1, I confirm AudioFire12 cannot correctly response to some commands with firmware version 5.0 or later. This is also confirmed by FFADO. So this driver implement way 2. The address for response gives an issue. When this driver allocate own callback function into the address, then no one can allocate its own callback function. This situation is not good for applications in user-land. This issue is solved in later commit. I note there is a command to change the address for response if the device supports. But this driver uses default value. So users should not execute this command as long as hoping this driver works correctly. Signed-off-by: Takashi Sakamoto <o-t...@sa...> --- sound/firewire/Kconfig | 1 + sound/firewire/fireworks/Makefile | 2 +- sound/firewire/fireworks/fireworks.c | 74 ++++- sound/firewire/fireworks/fireworks.h | 120 +++++++ sound/firewire/fireworks/fireworks_command.c | 395 +++++++++++++++++++++++ sound/firewire/fireworks/fireworks_transaction.c | 191 +++++++++++ 6 files changed, 773 insertions(+), 10 deletions(-) create mode 100644 sound/firewire/fireworks/fireworks_command.c create mode 100644 sound/firewire/fireworks/fireworks_transaction.c diff --git a/sound/firewire/Kconfig b/sound/firewire/Kconfig index 8cd4f1f..0b85ebd 100644 --- a/sound/firewire/Kconfig +++ b/sound/firewire/Kconfig @@ -63,6 +63,7 @@ config SND_SCS1X config SND_FIREWORKS tristate "Echo Fireworks board module support" + select SND_FIREWIRE_LIB help Say Y here to include support for FireWire devices based on Echo Digital Audio Fireworks board: diff --git a/sound/firewire/fireworks/Makefile b/sound/firewire/fireworks/Makefile index 99f6fc3..a6ce214 100644 --- a/sound/firewire/fireworks/Makefile +++ b/sound/firewire/fireworks/Makefile @@ -1,2 +1,2 @@ -snd-fireworks-objs := fireworks.o +snd-fireworks-objs := fireworks_transaction.o fireworks_command.o fireworks.o obj-m += snd-fireworks.o diff --git a/sound/firewire/fireworks/fireworks.c b/sound/firewire/fireworks/fireworks.c index 33a0acb..6c101f9 100644 --- a/sound/firewire/fireworks/fireworks.c +++ b/sound/firewire/fireworks/fireworks.c @@ -59,6 +59,46 @@ static unsigned int devices_used; /* unknown as product */ #define MODEL_GIBSON_GOLDTOP 0x00afb9 +/* part of hardware capability flags */ +#define FLAG_RESP_ADDR_CHANGABLE 0 + +static int +get_hardware_info(struct snd_efw *efw) +{ + struct fw_device *fw_dev = fw_parent_device(efw->unit); + struct snd_efw_hwinfo *hwinfo; + char version[12] = {0}; + int err; + + hwinfo = kzalloc(sizeof(struct snd_efw_hwinfo), GFP_KERNEL); + if (hwinfo == NULL) + return -ENOMEM; + + err = snd_efw_command_get_hwinfo(efw, hwinfo); + if (err < 0) + goto end; + + /* firmware version for communication chipset */ + err = sprintf(version, "%u.%u", + (hwinfo->arm_version >> 24) & 0xff, + (hwinfo->arm_version >> 16) & 0xff); + + strcpy(efw->card->driver, "Fireworks"); + strcpy(efw->card->shortname, hwinfo->model_name); + snprintf(efw->card->longname, sizeof(efw->card->longname), + "%s %s v%s, GUID %08x%08x at %s, S%d", + hwinfo->vendor_name, hwinfo->model_name, version, + hwinfo->guid_hi, hwinfo->guid_lo, + dev_name(&efw->unit->device), 100 << fw_dev->max_speed); + strcpy(efw->card->mixername, hwinfo->model_name); + + if (hwinfo->flags & BIT(FLAG_RESP_ADDR_CHANGABLE)) + efw->resp_addr_changable = true; +end: + kfree(hwinfo); + return err; +} + static void efw_card_free(struct snd_card *card) { @@ -107,27 +147,31 @@ efw_probe(struct fw_unit *unit, mutex_init(&efw->mutex); spin_lock_init(&efw->lock); - strcpy(efw->card->driver, "Fireworks"); - strcpy(efw->card->shortname, "Fireworks"); - strcpy(efw->card->longname, "Fireworks"); - strcpy(efw->card->mixername, "Fireworks"); + err = get_hardware_info(efw); + if (err < 0) + goto error; snd_card_set_dev(card, &unit->device); err = snd_card_register(card); - if (err < 0) { - snd_card_free(card); - goto end; - } + if (err < 0) + goto error; + dev_set_drvdata(&unit->device, efw); devices_used |= BIT(card_index); efw->card_index = card_index; end: mutex_unlock(&devices_mutex); return err; +error: + snd_card_free(card); + mutex_unlock(&devices_mutex); + return err; } static void efw_update(struct fw_unit *unit) { + struct snd_efw *efw = dev_get_drvdata(&unit->device); + snd_efw_transaction_bus_reset(efw->unit); return; } @@ -171,11 +215,23 @@ static struct fw_driver efw_driver = { static int __init snd_efw_init(void) { - return driver_register(&efw_driver.driver); + int err; + + err = snd_efw_transaction_register(); + if (err < 0) + goto end; + + err = driver_register(&efw_driver.driver); + if (err < 0) + snd_efw_transaction_unregister(); + +end: + return err; } static void __exit snd_efw_exit(void) { + snd_efw_transaction_unregister(); driver_unregister(&efw_driver.driver); mutex_destroy(&devices_mutex); } diff --git a/sound/firewire/fireworks/fireworks.h b/sound/firewire/fireworks/fireworks.h index 9e29ee4..751302d 100644 --- a/sound/firewire/fireworks/fireworks.h +++ b/sound/firewire/fireworks/fireworks.h @@ -20,6 +20,19 @@ #include <sound/core.h> #include <sound/initval.h> +#include <sound/pcm.h> + +#include "../cmp.h" +#include "../lib.h" + +#define SND_EFW_MUITIPLIER_MODES 3 +#define HWINFO_NAME_SIZE_BYTES 32 +#define HWINFO_MAX_CAPS_GROUPS 8 + +struct snd_efw_phys_grp { + u8 type; /* see enum snd_efw_grp_type */ + u8 count; +} __packed; struct snd_efw { struct snd_card *card; @@ -28,7 +41,114 @@ struct snd_efw { struct mutex mutex; spinlock_t lock; + + /* for transaction */ + u32 seqnum; + bool resp_addr_changable; +}; + +struct snd_efw_transaction { + u32 length; + u32 version; + u32 seqnum; + u32 category; + u32 command; + u32 status; + u32 params[0]; +}; +int snd_efw_transaction_run(struct fw_unit *unit, + const void *cmd, unsigned int cmd_size, + void *resp, unsigned int resp_size, u32 seqnum); +int snd_efw_transaction_register(void); +void snd_efw_transaction_unregister(void); +void snd_efw_transaction_bus_reset(struct fw_unit *unit); + +struct snd_efw_hwinfo { + u32 flags; + u32 guid_hi; + u32 guid_lo; + u32 type; + u32 version; + char vendor_name[HWINFO_NAME_SIZE_BYTES]; + char model_name[HWINFO_NAME_SIZE_BYTES]; + u32 supported_clocks; + u32 amdtp_rx_pcm_channels; + u32 amdtp_tx_pcm_channels; + u32 phys_out; + u32 phys_in; + u32 phys_out_grp_count; + struct snd_efw_phys_grp phys_out_grps[HWINFO_MAX_CAPS_GROUPS]; + u32 phys_in_grp_count; + struct snd_efw_phys_grp phys_in_grps[HWINFO_MAX_CAPS_GROUPS]; + u32 midi_out_ports; + u32 midi_in_ports; + u32 max_sample_rate; + u32 min_sample_rate; + u32 dsp_version; + u32 arm_version; + u32 mixer_playback_channels; + u32 mixer_capture_channels; + u32 fpga_version; + u32 amdtp_rx_pcm_channels_2x; + u32 amdtp_tx_pcm_channels_2x; + u32 amdtp_rx_pcm_channels_4x; + u32 amdtp_tx_pcm_channels_4x; + u32 reserved[16]; +} __packed; +enum snd_efw_grp_type { + SND_EFW_CH_TYPE_ANALOG = 0, + SND_EFW_CH_TYPE_SPDIF = 1, + SND_EFW_CH_TYPE_ADAT = 2, + SND_EFW_CH_TYPE_SPDIF_OR_ADAT = 3, + SND_EFW_CH_TYPE_ANALOG_MIRRORING = 4, + SND_EFW_CH_TYPE_HEADPHONES = 5, + SND_EFW_CH_TYPE_I2S = 6, + SND_EFW_CH_TYPE_GUITAR = 7, + SND_EFW_CH_TYPE_PIEZO_GUITAR = 8, + SND_EFW_CH_TYPE_GUITAR_STRING = 9, + SND_EFW_CH_TYPE_VIRTUAL = 0x10000, + SND_EFW_CH_TYPE_DUMMY +}; +struct snd_efw_phys_meters { + u32 status; /* guitar state/midi signal/clock input detect */ + u32 reserved0; + u32 reserved1; + u32 reserved2; + u32 reserved3; + u32 out_meters; + u32 in_meters; + u32 reserved4; + u32 reserved5; + u32 values[0]; +} __packed; +enum snd_efw_clock_source { + SND_EFW_CLOCK_SOURCE_INTERNAL = 0, + SND_EFW_CLOCK_SOURCE_SYTMATCH = 1, + SND_EFW_CLOCK_SOURCE_WORDCLOCK = 2, + SND_EFW_CLOCK_SOURCE_SPDIF = 3, + SND_EFW_CLOCK_SOURCE_ADAT_1 = 4, + SND_EFW_CLOCK_SOURCE_ADAT_2 = 5, + SND_EFW_CLOCK_SOURCE_CONTINUOUS = 6 /* internal variable clock */ +}; +enum snd_efw_transport_mode { + SND_EFW_TRANSPORT_MODE_WINDOWS = 0, + SND_EFW_TRANSPORT_MODE_IEC61883 = 1, }; +int snd_efw_command_identify(struct snd_efw *efw); +int snd_efw_command_set_resp_addr(struct snd_efw *efw, + u16 addr_high, u32 addr_low); +int snd_efw_command_set_tx_mode(struct snd_efw *efw, unsigned int mode); +int snd_efw_command_get_hwinfo(struct snd_efw *efw, + struct snd_efw_hwinfo *hwinfo); +int snd_efw_command_get_phys_meters(struct snd_efw *efw, + struct snd_efw_phys_meters *meters, + unsigned int len); +int snd_efw_command_get_clock_source(struct snd_efw *efw, + enum snd_efw_clock_source *source); +int snd_efw_command_set_clock_source(struct snd_efw *efw, + enum snd_efw_clock_source source); +int snd_efw_command_get_sampling_rate(struct snd_efw *efw, unsigned int *rate); +int snd_efw_command_set_sampling_rate(struct snd_efw *efw, unsigned int rate); #define SND_EFW_DEV_ENTRY(vendor, model) \ { \ diff --git a/sound/firewire/fireworks/fireworks_command.c b/sound/firewire/fireworks/fireworks_command.c new file mode 100644 index 0000000..9b19e3e --- /dev/null +++ b/sound/firewire/fireworks/fireworks_command.c @@ -0,0 +1,395 @@ +/* + * fireworks_command.c - a part of driver for Fireworks based devices + * + * Copyright (c) 2013 Takashi Sakamoto <o-t...@sa...> + * + * Licensed under the terms of the GNU General Public License, version 2. + */ + +#include "./fireworks.h" + +/* + * This driver uses transaction version 1 or later to use extended hardware + * information. Then too old devices are not available. + * + * Each commands are not required to have continuous sequence numbers. This + * number is just used to match command and response. + * + * This module support a part of commands. Please see FFADO if you want to see + * whole commands. But there are some commands which FFADO don't implement. + * + * Fireworks also supports AV/C general commands and AV/C Stream Format + * Information commands. But this module don't use them. + */ + +#define EFW_TRANSACTION_SEQNUM_MAX ((u32)~0) + +/* for clock source and sampling rate */ +struct efc_clock { + u32 source; + u32 sampling_rate; + u32 index; +}; + +/* command categories */ +enum efc_category { + EFC_CAT_HWINFO = 0, + EFC_CAT_TRANSPORT = 2, + EFC_CAT_HWCTL = 3, +}; + +/* hardware info category commands */ +enum efc_cmd_hwinfo { + EFC_CMD_HWINFO_GET_CAPS = 0, + EFC_CMD_HWINFO_GET_POLLED = 1, + EFC_CMD_HWINFO_SET_RESP_ADDR = 2 +}; + +enum efc_cmd_transport { + EFC_CMD_TRANSPORT_SET_TX_MODE = 0 +}; + +/* hardware control category commands */ +enum efc_cmd_hwctl { + EFC_CMD_HWCTL_SET_CLOCK = 0, + EFC_CMD_HWCTL_GET_CLOCK = 1, + EFC_CMD_HWCTL_IDENTIFY = 5 +}; + +/* return values in response */ +enum efr_status { + EFC_RETVAL_OK = 0, + EFC_RETVAL_BAD = 1, + EFC_RETVAL_BAD_COMMAND = 2, + EFC_RETVAL_COMM_ERR = 3, + EFC_RETVAL_BAD_QUAD_COUNT = 4, + EFC_RETVAL_UNSUPPORTED = 5, + EFC_RETVAL_1394_TIMEOUT = 6, + EFC_RETVAL_DSP_TIMEOUT = 7, + EFC_RETVAL_BAD_RATE = 8, + EFC_RETVAL_BAD_CLOCK = 9, + EFC_RETVAL_BAD_CHANNEL = 10, + EFC_RETVAL_BAD_PAN = 11, + EFC_RETVAL_FLASH_BUSY = 12, + EFC_RETVAL_BAD_MIRROR = 13, + EFC_RETVAL_BAD_LED = 14, + EFC_RETVAL_BAD_PARAMETER = 15, + EFC_RETVAL_INCOMPLETE = 0x80000000 +}; + +static const char *const efr_status_names[] = { + [EFC_RETVAL_OK] = "OK", + [EFC_RETVAL_BAD] = "bad", + [EFC_RETVAL_BAD_COMMAND] = "bad command", + [EFC_RETVAL_COMM_ERR] = "comm err", + [EFC_RETVAL_BAD_QUAD_COUNT] = "bad quad count", + [EFC_RETVAL_UNSUPPORTED] = "unsupported", + [EFC_RETVAL_1394_TIMEOUT] = "1394 timeout", + [EFC_RETVAL_DSP_TIMEOUT] = "DSP timeout", + [EFC_RETVAL_BAD_RATE] = "bad rate", + [EFC_RETVAL_BAD_CLOCK] = "bad clock", + [EFC_RETVAL_BAD_CHANNEL] = "bad channel", + [EFC_RETVAL_BAD_PAN] = "bad pan", + [EFC_RETVAL_FLASH_BUSY] = "flash busy", + [EFC_RETVAL_BAD_MIRROR] = "bad mirror", + [EFC_RETVAL_BAD_LED] = "bad LED", + [EFC_RETVAL_BAD_PARAMETER] = "bad parameter", + [EFC_RETVAL_BAD_PARAMETER + 1] = "incomplete" +}; + +static int +efw_transaction(struct snd_efw *efw, unsigned int category, + unsigned int command, + const __be32 *params, unsigned int param_quads, + const __be32 *resp, unsigned int resp_quads) +{ + struct snd_efw_transaction *header; + __be32 *buf; + u32 seqnum; + unsigned int i, buf_bytes, cmd_bytes; + int err; + + /* calculate buffer size*/ + buf_bytes = sizeof(struct snd_efw_transaction) + + max(param_quads, resp_quads) * sizeof(u32); + + /* keep buffer */ + buf = kzalloc(buf_bytes, GFP_KERNEL); + if (buf == NULL) + return -ENOMEM; + + /* to keep consistency of sequence number */ + spin_lock(&efw->lock); + if (efw->seqnum + 1 >= EFW_TRANSACTION_SEQNUM_MAX) + efw->seqnum = 0; + else + efw->seqnum += 2; + seqnum = efw->seqnum; + spin_unlock(&efw->lock); + + /* fill transaction header fields */ + cmd_bytes = sizeof(struct snd_efw_transaction) + + param_quads * sizeof(u32); + header = (struct snd_efw_transaction *)buf; + header->length = cmd_bytes / sizeof(u32); + header->version = 1; + header->seqnum = seqnum; + header->category = category; + header->command = command; + header->status = 0; + for (i = 0; i < sizeof(struct snd_efw_transaction) / sizeof(u32); i++) + cpu_to_be32s(&buf[i]); + + /* fill transaction command parameters */ + for (i = 0; i < param_quads; i++) + header->params[i] = params[i]; + + err = snd_efw_transaction_run(efw->unit, buf, cmd_bytes, + buf, buf_bytes, seqnum); + if (err < 0) + goto end; + + /* check transaction header fields */ + for (i = 0; i < sizeof(struct snd_efw_transaction) / sizeof(u32); i++) + be32_to_cpus(&buf[i]); + if ((header->version < 1) || + (header->category != category) || + (header->command != command) || + (header->status != EFC_RETVAL_OK)) { + dev_err(&efw->unit->device, "EFC failed [%u/%u]: %s\n", + header->category, header->command, + efr_status_names[header->status]); + err = -EIO; + goto end; + } + + /* fill transaction response parameters */ + if (resp != NULL) { + memset((void *)resp, 0, resp_quads * sizeof(u32)); + resp_quads = min_t(unsigned int, resp_quads, + header->length - + sizeof(struct snd_efw_transaction) / + sizeof(u32)); + memcpy((void *)resp, &buf[6], resp_quads * sizeof(u32)); + } +end: + kfree(buf); + return err; +} + +/* just blink LEDs on the device */ +int snd_efw_command_identify(struct snd_efw *efw) +{ + return efw_transaction(efw, EFC_CAT_HWCTL, + EFC_CMD_HWCTL_IDENTIFY, + NULL, 0, NULL, 0); +} + +/* + * The address in host system for EFC response is changable when the device + * supports. struct hwinfo.flags includes its flag. The default is + * INITIAL_MEMORY_SPACE_EFC_RESPONSE + */ +int snd_efw_command_set_resp_addr(struct snd_efw *efw, + u16 addr_high, u32 addr_low) +{ + __be32 addr[2]; + + addr[0] = cpu_to_be32(addr_high); + addr[1] = cpu_to_be32(addr_low); + + if (!efw->resp_addr_changable) + return -ENOSYS; + + return efw_transaction(efw, EFC_CAT_HWCTL, + EFC_CMD_HWINFO_SET_RESP_ADDR, + addr, 2, NULL, 0); +} + +/* + * This is for timestamp processing. In Windows mode, all 32bit fields of second + * CIP header in AMDTP transmit packet is used for 'presentation timestamp'. In + * 'no data' packet the value of this field is 0x90ffffff. + */ +int snd_efw_command_set_tx_mode(struct snd_efw *efw, + enum snd_efw_transport_mode mode) +{ + __be32 param = cpu_to_be32(mode); + return efw_transaction(efw, EFC_CAT_TRANSPORT, + EFC_CMD_TRANSPORT_SET_TX_MODE, + ¶m, 1, NULL, 0); +} + +int snd_efw_command_get_hwinfo(struct snd_efw *efw, + struct snd_efw_hwinfo *hwinfo) +{ + int err; + + err = efw_transaction(efw, EFC_CAT_HWINFO, + EFC_CMD_HWINFO_GET_CAPS, + NULL, 0, (__be32 *)hwinfo, + sizeof(*hwinfo) / sizeof(u32)); + if (err < 0) + goto end; + + be32_to_cpus(&hwinfo->flags); + be32_to_cpus(&hwinfo->guid_hi); + be32_to_cpus(&hwinfo->guid_lo); + be32_to_cpus(&hwinfo->type); + be32_to_cpus(&hwinfo->version); + be32_to_cpus(&hwinfo->supported_clocks); + be32_to_cpus(&hwinfo->amdtp_rx_pcm_channels); + be32_to_cpus(&hwinfo->amdtp_tx_pcm_channels); + be32_to_cpus(&hwinfo->phys_out); + be32_to_cpus(&hwinfo->phys_in); + be32_to_cpus(&hwinfo->phys_out_grp_count); + be32_to_cpus(&hwinfo->phys_in_grp_count); + be32_to_cpus(&hwinfo->midi_out_ports); + be32_to_cpus(&hwinfo->midi_in_ports); + be32_to_cpus(&hwinfo->max_sample_rate); + be32_to_cpus(&hwinfo->min_sample_rate); + be32_to_cpus(&hwinfo->dsp_version); + be32_to_cpus(&hwinfo->arm_version); + be32_to_cpus(&hwinfo->mixer_playback_channels); + be32_to_cpus(&hwinfo->mixer_capture_channels); + be32_to_cpus(&hwinfo->fpga_version); + be32_to_cpus(&hwinfo->amdtp_rx_pcm_channels_2x); + be32_to_cpus(&hwinfo->amdtp_tx_pcm_channels_2x); + be32_to_cpus(&hwinfo->amdtp_rx_pcm_channels_4x); + be32_to_cpus(&hwinfo->amdtp_tx_pcm_channels_4x); + + /* ensure terminated */ + hwinfo->vendor_name[HWINFO_NAME_SIZE_BYTES - 1] = '\0'; + hwinfo->model_name[HWINFO_NAME_SIZE_BYTES - 1] = '\0'; +end: + return err; +} + +int snd_efw_command_get_phys_meters(struct snd_efw *efw, + struct snd_efw_phys_meters *meters, + unsigned int len) +{ + __be32 *buf = (__be32 *)meters; + unsigned int i; + int err; + + err = efw_transaction(efw, EFC_CAT_HWINFO, + EFC_CMD_HWINFO_GET_POLLED, + NULL, 0, (__be32 *)meters, len / sizeof(u32)); + if (err >= 0) + for (i = 0; i < len / sizeof(u32); i++) + be32_to_cpus(&buf[i]); + + return err; +} + +static int +command_get_clock(struct snd_efw *efw, struct efc_clock *clock) +{ + int err; + + err = efw_transaction(efw, EFC_CAT_HWCTL, + EFC_CMD_HWCTL_GET_CLOCK, + NULL, 0, + (__be32 *)clock, + sizeof(struct efc_clock) / sizeof(u32)); + if (err >= 0) { + be32_to_cpus(&clock->source); + be32_to_cpus(&clock->sampling_rate); + be32_to_cpus(&clock->index); + } + + return err; +} + +/* give UINT_MAX if set nothing */ +static int +command_set_clock(struct snd_efw *efw, + unsigned int source, unsigned int rate) +{ + struct efc_clock clock = {0}; + int err; + + /* check arguments */ + if ((source == UINT_MAX) && (rate == UINT_MAX)) { + err = -EINVAL; + goto end; + } + + /* get current status */ + err = command_get_clock(efw, &clock); + if (err < 0) + goto end; + + /* no need */ + if ((clock.source == source) && (clock.sampling_rate == rate)) + goto end; + + /* set params */ + if ((source != UINT_MAX) && (clock.source != source)) + clock.source = source; + if ((rate != UINT_MAX) && (clock.sampling_rate != rate)) + clock.sampling_rate = rate; + clock.index = 0; + + cpu_to_be32s(&clock.source); + cpu_to_be32s(&clock.sampling_rate); + cpu_to_be32s(&clock.index); + + err = efw_transaction(efw, EFC_CAT_HWCTL, + EFC_CMD_HWCTL_SET_CLOCK, + (__be32 *)&clock, + sizeof(struct efc_clock) / sizeof(u32), + NULL, 0); + if (err < 0) + goto end; + + /* + * With firmware version 5.8, just after changing clock state, these + * parameters are not immediately retrieved by get command. In my + * trial, there needs to be 100msec to get changed parameters. + */ + msleep(150); +end: + return err; +} + +int snd_efw_command_get_clock_source(struct snd_efw *efw, + enum snd_efw_clock_source *source) +{ + int err; + struct efc_clock clock = {0}; + + err = command_get_clock(efw, &clock); + if (err >= 0) + *source = clock.source; + + return err; +} + +int snd_efw_command_set_clock_source(struct snd_efw *efw, + enum snd_efw_clock_source source) +{ + return command_set_clock(efw, source, UINT_MAX); +} + +int snd_efw_command_get_sampling_rate(struct snd_efw *efw, + unsigned int *rate) +{ + int err; + struct efc_clock clock = {0}; + + err = command_get_clock(efw, &clock); + if (err >= 0) + *rate = clock.sampling_rate; + + return err; +} + +int +snd_efw_command_set_sampling_rate(struct snd_efw *efw, + unsigned int rate) +{ + return command_set_clock(efw, UINT_MAX, rate); +} + diff --git a/sound/firewire/fireworks/fireworks_transaction.c b/sound/firewire/fireworks/fireworks_transaction.c new file mode 100644 index 0000000..c2f499e --- /dev/null +++ b/sound/firewire/fireworks/fireworks_transaction.c @@ -0,0 +1,191 @@ +/* + * fireworks_transaction.c - a part of driver for Fireworks based devices + * + * Copyright (c) 2013 Takashi Sakamoto <o-t...@sa...> + * + * Licensed under the terms of the GNU General Public License, version 2. + */ + +/* + * Fireworks have its own transaction. The transaction can be delivered by AV/C + * Vendor Specific command. But at least Windows driver and firmware version 5.5 + * or later don't use it. + * + * Transaction substance: + * At first, 6 data exist. Following to the 6 data, parameters for each + * commands exists. All of parameters are 32 bit alighed to big endian. + * data[0]: Length of transaction substance + * data[1]: Transaction version + * data[2]: Sequence number. This is incremented by the device + * data[3]: transaction category + * data[4]: transaction command + * data[5]: return value in response. + * data[6-]: parameters + * + * Transaction address: + * command: 0xecc000000000 + * response: 0xecc080000000 (default) + * + * I note that the address for response can be changed by command. But this + * module uses the default address. + */ +#include "./fireworks.h" + +#define MEMORY_SPACE_EFW_COMMAND 0xecc000000000 +#define MEMORY_SPACE_EFW_RESPONSE 0xecc080000000 +/* this is for juju convinience? */ +#define MEMORY_SPACE_EFW_END 0xecc080000200 + +#define ERROR_RETRIES 3 +#define ERROR_DELAY_MS 5 +#define EFC_TIMEOUT_MS 125 + +static DEFINE_SPINLOCK(transaction_queues_lock); +static LIST_HEAD(transaction_queues); + +enum transaction_queue_state { + STATE_PENDING, + STATE_BUS_RESET, + STATE_COMPLETE +}; + +struct transaction_queue { + struct list_head list; + struct fw_unit *unit; + void *buf; + unsigned int size; + u32 seqnum; + enum transaction_queue_state state; + wait_queue_head_t wait; +}; + +int snd_efw_transaction_run(struct fw_unit *unit, + const void *cmd, unsigned int cmd_size, + void *resp, unsigned int resp_size, u32 seqnum) +{ + struct transaction_queue t; + unsigned int tries; + int ret; + + t.unit = unit; + t.buf = resp; + t.size = resp_size; + t.seqnum = seqnum + 1; + t.state = STATE_PENDING; + init_waitqueue_head(&t.wait); + + spin_lock_irq(&transaction_queues_lock); + list_add_tail(&t.list, &transaction_queues); + spin_unlock_irq(&transaction_queues_lock); + + tries = 0; + do { + ret = snd_fw_transaction(unit, TCODE_WRITE_BLOCK_REQUEST, + MEMORY_SPACE_EFW_COMMAND, + (void *)cmd, cmd_size, 0); + if (ret < 0) + break; + + wait_event_timeout(t.wait, t.state != STATE_PENDING, + msecs_to_jiffies(EFC_TIMEOUT_MS)); + + if (t.state == STATE_COMPLETE) { + ret = t.size; + break; + } else if (t.state == STATE_BUS_RESET) { + msleep(ERROR_DELAY_MS); + } else if (++tries >= ERROR_RETRIES) { + dev_err(&t.unit->device, "EFC command timed out\n"); + ret = -EIO; + break; + } + } while (1); + + spin_lock_irq(&transaction_queues_lock); + list_del(&t.list); + spin_unlock_irq(&transaction_queues_lock); + + return ret; +} + +static void +efw_response(struct fw_card *card, struct fw_request *request, + int tcode, int destination, int source, + int generation, unsigned long long offset, + void *data, size_t length, void *callback_data) +{ + struct fw_device *device; + struct transaction_queue *t; + unsigned long flags; + int rcode; + u32 seqnum; + + rcode = RCODE_TYPE_ERROR; + if (length < sizeof(struct snd_efw_transaction)) { + rcode = RCODE_DATA_ERROR; + goto end; + } else if (offset != MEMORY_SPACE_EFW_RESPONSE) { + rcode = RCODE_ADDRESS_ERROR; + goto end; + } + + seqnum = be32_to_cpu(((struct snd_efw_transaction *)data)->seqnum); + + spin_lock_irqsave(&transaction_queues_lock, flags); + list_for_each_entry(t, &transaction_queues, list) { + device = fw_parent_device(t->unit); + if ((device->card != card) || + (device->generation != generation)) + continue; + smp_rmb(); /* node_id vs. generation */ + if (device->node_id != source) + continue; + + if ((t->state == STATE_PENDING) && (t->seqnum == seqnum)) { + t->state = STATE_COMPLETE; + t->size = min_t(unsigned int, length, t->size); + memcpy(t->buf, data, t->size); + wake_up(&t->wait); + rcode = RCODE_COMPLETE; + } + } + spin_unlock_irqrestore(&transaction_queues_lock, flags); +end: + fw_send_response(card, request, rcode); +} + +void snd_efw_transaction_bus_reset(struct fw_unit *unit) +{ + struct transaction_queue *t; + + spin_lock_irq(&transaction_queues_lock); + list_for_each_entry(t, &transaction_queues, list) { + if ((t->unit == unit) && + (t->state == STATE_PENDING)) { + t->state = STATE_BUS_RESET; + wake_up(&t->wait); + } + } + spin_unlock_irq(&transaction_queues_lock); +} + +static struct fw_address_handler resp_register_handler = { + .length = MEMORY_SPACE_EFW_END - MEMORY_SPACE_EFW_RESPONSE, + .address_callback = efw_response +}; + +int snd_efw_transaction_register(void) +{ + static const struct fw_address_region resp_register_region = { + .start = MEMORY_SPACE_EFW_RESPONSE, + .end = MEMORY_SPACE_EFW_END + }; + return fw_core_add_address_handler(&resp_register_handler, + &resp_register_region); +} + +void snd_efw_transaction_unregister(void) +{ + WARN_ON(!list_empty(&transaction_queues)); + fw_core_remove_address_handler(&resp_register_handler); +} -- 1.8.3.2 |
From: Takashi S. <o-t...@sa...> - 2014-01-29 13:45:55
|
This commit add a new driver for BeBoB based devices with no functionality. This driver just create/remove card instance according to callbacks. BeBoB is 'BridgeCo enhanced Breakout Box'. This is installed to firewire devices with DM1000/1000E/1100/1500 chipset. It gives common way for host system to handle BeBoB based devices. Current supported devices: - Edirol FA-66/FA-101 - PreSonus FIREBOX/FIREPOD/FP10/Inspire1394 - BridgeCo RDAudio1/Audio5 - Mackie Onyx 1220/1620/1640 (Firewire I/O Card) - Mackie d.2 (Firewire Option) - Stanton FinalScratch 2 (ScratchAmp) - Tascam IF-FW DM - Behringer XENIX UFX 1204/1604 - Behringer Digital Mixer X32 series (X-UF Card) - Apogee Rosetta 200/Rosetta 400 (X-FireWire card) - Apogee DA-16X/AD-16X/DD-16X (X-FireWire card) - Apogee Ensemble - ESI Quotafire610 - AcousticReality eARMasterOne - CME MatrixKFW - Phonix Helix Board 12 MkII/18 MkII/24 MkII - Phonic Helix Board 12 Universal/18 Universal/24 Universal - Lynx Aurora 8/16 (LT-FW) - ICON FireXon - PrismSound Orpheus/ADA-8XR Devices possible to be supported if identifying IDs: - Apogee Mini-Me Firewire/Mini-DAC Firewire - Behringer F-Control Audio 610/1616 - Cakewalk Sonar Power Studio 66 - CME UF400e - ESI Quotafire XL - Infrasonic DewX/Windy6 - Mackie Digital X Bus x.200/400 - Phonic Helix Board 12/18/24 - Phonic FireFly 202/302 - Rolf Spuler Firewire Guitar Signed-off-by: Takashi Sakamoto <o-t...@sa...> --- sound/firewire/Kconfig | 30 +++++ sound/firewire/Makefile | 1 + sound/firewire/bebob/Makefile | 2 + sound/firewire/bebob/bebob.c | 299 ++++++++++++++++++++++++++++++++++++++++++ sound/firewire/bebob/bebob.h | 64 +++++++++ 5 files changed, 396 insertions(+) create mode 100644 sound/firewire/bebob/Makefile create mode 100644 sound/firewire/bebob/bebob.c create mode 100644 sound/firewire/bebob/bebob.h diff --git a/sound/firewire/Kconfig b/sound/firewire/Kconfig index 875af02..9027b23 100644 --- a/sound/firewire/Kconfig +++ b/sound/firewire/Kconfig @@ -79,4 +79,34 @@ config SND_FIREWORKS To compile this driver as a module, choose M here: the module will be called snd-fireworks. +config SND_BEBOB + tristate "BridgeCo DM1000/1500 with BeBoB firmware" + select SND_FIREWIRE_LIB + help + Say Y here to include support for FireWire devices based + on BridgeCo DM1000/1500 with BeBoB firmware: + * Edirol FA-66/FA-101 + * PreSonus FIREBOX/FIREPOD/FP10/Inspire1394 + * BridgeCo RDAudio1/Audio5 + * Mackie Onyx 1220/1620/1640 (Firewire I/O Card) + * Mackie d.2 (Firewire Option) + * Stanton FinalScratch 2 (ScratchAmp) + * Tascam IF-FW/DM + * Behringer XENIX UFX 1204/1604 + * Behringer Digital Mixer X32 series (X-UF Card) + * Apogee Rosetta 200/400 (X-FireWire card) + * Apogee DA/AD/DD-16X (X-FireWire card) + * Apogee Ensemble + * ESI Quotafire610 + * AcousticReality eARMasterOne + * CME MatrixKFW + * Phonic Helix Board 12 MkII/18 MkII/24 MkII + * Phonic Helix Board 12 Universal/18 Universal/24 Universal + * Lynx Aurora 8/16 (LT-FW) + * ICON FireXon + * PrismSound Orpheus/ADA-8XR + + To compile this driver as a module, choose M here: the module + will be called snd-bebob. + endif # SND_FIREWIRE diff --git a/sound/firewire/Makefile b/sound/firewire/Makefile index 5cd39dc..fad8d49 100644 --- a/sound/firewire/Makefile +++ b/sound/firewire/Makefile @@ -11,3 +11,4 @@ obj-$(CONFIG_SND_FIREWIRE_SPEAKERS) += snd-firewire-speakers.o obj-$(CONFIG_SND_ISIGHT) += snd-isight.o obj-$(CONFIG_SND_SCS1X) += snd-scs1x.o obj-$(CONFIG_SND_FIREWORKS) += fireworks/ +obj-$(CONFIG_SND_BEBOB) += bebob/ diff --git a/sound/firewire/bebob/Makefile b/sound/firewire/bebob/Makefile new file mode 100644 index 0000000..c6f0141 --- /dev/null +++ b/sound/firewire/bebob/Makefile @@ -0,0 +1,2 @@ +snd-bebob-objs := bebob.o +obj-m += snd-bebob.o diff --git a/sound/firewire/bebob/bebob.c b/sound/firewire/bebob/bebob.c new file mode 100644 index 0000000..5619bd6 --- /dev/null +++ b/sound/firewire/bebob/bebob.c @@ -0,0 +1,299 @@ +/* + * bebob.c - a part of driver for BeBoB based devices + * + * Copyright (c) 2013 Takashi Sakamoto + * + * Licensed under the terms of the GNU General Public License, version 2. + */ + +/* + * BeBoB is 'BridgeCo enhanced Breakout Box'. This is installed to firewire + * devices with DM1000/1000E/1100/1500 chipset. It gives common way for host + * system to handle BeBoB based devices. + */ + +#include "bebob.h" + +MODULE_DESCRIPTION("BridgeCo BeBoB driver"); +MODULE_AUTHOR("Takashi Sakamoto <o-t...@sa...>"); +MODULE_LICENSE("GPL v2"); + +static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; +static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; +static bool enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP; + +module_param_array(index, int, NULL, 0444); +MODULE_PARM_DESC(index, "card index"); +module_param_array(id, charp, NULL, 0444); +MODULE_PARM_DESC(id, "ID string"); +module_param_array(enable, bool, NULL, 0444); +MODULE_PARM_DESC(enable, "enable BeBoB sound card"); + +static DEFINE_MUTEX(devices_mutex); +static unsigned int devices_used; + +/* Offsets from information register. */ +#define INFO_OFFSET_GUID 0x10 +#define INFO_OFFSET_HW_MODEL_ID 0x18 +#define INFO_OFFSET_HW_MODEL_REVISION 0x1c + +#define VEN_EDIROL 0x000040ab +#define VEN_PRESONUS 0x00000a92 +#define VEN_BRIDGECO 0x000007f5 +#define VEN_MACKIE 0x0000000f +#define VEN_STANTON 0x00001260 +#define VEN_TASCAM 0x0000022e +#define VEN_BEHRINGER 0x00001564 +#define VEN_APOGEE 0x000003db +#define VEN_ESI 0x00000f1b +#define VEN_ACOUSTIC 0x00000002 +#define VEN_CME 0x0000000a +#define VEN_PHONIC 0x00001496 +#define VEN_LYNX 0x000019e5 +#define VEN_ICON 0x00001a9e +#define VEN_PRISMSOUND 0x00001198 + +static int +name_device(struct snd_bebob *bebob, unsigned int vendor_id) +{ + struct fw_device *fw_dev = fw_parent_device(bebob->unit); + char vendor[24] = {0}; + char model[24] = {0}; + u32 id; + u32 data[2] = {0}; + u32 revision; + int err; + + /* get vendor name from root directory */ + err = fw_csr_string(fw_dev->config_rom + 5, CSR_VENDOR, + vendor, sizeof(vendor)); + if (err < 0) + goto end; + + /* get model name from unit directory */ + err = fw_csr_string(bebob->unit->directory, CSR_MODEL, + model, sizeof(model)); + if (err < 0) + goto end; + + /* get hardware id */ + err = snd_bebob_read_quad(bebob->unit, INFO_OFFSET_HW_MODEL_ID, + &id); + if (err < 0) + goto end; + + /* get hardware revision */ + err = snd_bebob_read_quad(bebob->unit, INFO_OFFSET_HW_MODEL_REVISION, + &revision); + if (err < 0) + goto end; + + /* get GUID */ + err = snd_bebob_read_block(bebob->unit, INFO_OFFSET_GUID, + data, sizeof(data)); + if (err < 0) + goto end; + + strcpy(bebob->card->driver, "BeBoB"); + strcpy(bebob->card->shortname, model); + snprintf(bebob->card->longname, sizeof(bebob->card->longname), + "%s %s (id:%d, rev:%d), GUID %08x%08x at %s, S%d", + vendor, model, id, revision, + data[0], data[1], dev_name(&bebob->unit->device), + 100 << fw_dev->max_speed); + strcpy(bebob->card->mixername, model); +end: + return err; +} + +static void +bebob_card_free(struct snd_card *card) +{ + struct snd_bebob *bebob = card->private_data; + + if (bebob->card_index >= 0) { + mutex_lock(&devices_mutex); + devices_used &= ~BIT(bebob->card_index); + mutex_unlock(&devices_mutex); + } + + mutex_destroy(&bebob->mutex); + + return; +} + +static int +bebob_probe(struct fw_unit *unit, + const struct ieee1394_device_id *entry) +{ + struct snd_card *card; + struct snd_bebob *bebob; + unsigned int card_index; + int err; + + mutex_lock(&devices_mutex); + + for (card_index = 0; card_index < SNDRV_CARDS; card_index++) { + if (!(devices_used & BIT(card_index)) && enable[card_index]) + break; + } + if (card_index >= SNDRV_CARDS) { + err = -ENOENT; + goto end; + } + + err = snd_card_create(index[card_index], id[card_index], + THIS_MODULE, sizeof(struct snd_bebob), &card); + if (err < 0) + goto end; + card->private_free = bebob_card_free; + + bebob = card->private_data; + bebob->card = card; + bebob->unit = unit; + bebob->card_index = -1; + mutex_init(&bebob->mutex); + spin_lock_init(&bebob->lock); + + err = name_device(bebob, entry->vendor_id); + if (err < 0) + goto error; + + snd_card_set_dev(card, &unit->device); + err = snd_card_register(card); + if (err < 0) { + snd_card_free(card); + goto error; + } + dev_set_drvdata(&unit->device, bebob); + devices_used |= BIT(card_index); + bebob->card_index = card_index; +end: + mutex_unlock(&devices_mutex); + return err; +error: + snd_card_free(card); + mutex_unlock(&devices_mutex); + return err; +} + +static void +bebob_update(struct fw_unit *unit) +{ + struct snd_bebob *bebob = dev_get_drvdata(&unit->device); + fcp_bus_reset(bebob->unit); +} + + +static void bebob_remove(struct fw_unit *unit) +{ + struct snd_bebob *bebob = dev_get_drvdata(&unit->device); + snd_card_disconnect(bebob->card); + snd_card_free_when_closed(bebob->card); +} + +static const struct ieee1394_device_id bebob_id_table[] = { + /* Edirol, FA-66 */ + SND_BEBOB_DEV_ENTRY(VEN_EDIROL, 0x00010049), + /* Edirol, FA-101 */ + SND_BEBOB_DEV_ENTRY(VEN_EDIROL, 0x00010048), + /* Presonus, FIREBOX */ + SND_BEBOB_DEV_ENTRY(VEN_PRESONUS, 0x00010000), + /* PreSonus, FIREPOD/FP10 */ + SND_BEBOB_DEV_ENTRY(VEN_PRESONUS, 0x00010066), + /* PreSonus, Inspire1394 */ + SND_BEBOB_DEV_ENTRY(VEN_PRESONUS, 0x00010001), + /* BridgeCo, RDAudio1 */ + SND_BEBOB_DEV_ENTRY(VEN_BRIDGECO, 0x00010048), + /* BridgeCo, Audio5 */ + SND_BEBOB_DEV_ENTRY(VEN_BRIDGECO, 0x00010049), + /* Mackie, Onyx 1220/1620/1640 (Firewire I/O Card) */ + SND_BEBOB_DEV_ENTRY(VEN_MACKIE, 0x00010065), + /* Mackie, d.2 (Firewire Option) */ + SND_BEBOB_DEV_ENTRY(VEN_MACKIE, 0x00010067), + /* Stanton, ScratchAmp */ + SND_BEBOB_DEV_ENTRY(VEN_STANTON, 0x00000001), + /* Tascam, IF-FW DM */ + SND_BEBOB_DEV_ENTRY(VEN_TASCAM, 0x00010067), + /* Behringer, XENIX UFX 1204 */ + SND_BEBOB_DEV_ENTRY(VEN_BEHRINGER, 0x00001204), + /* Behringer, XENIX UFX 1604 */ + SND_BEBOB_DEV_ENTRY(VEN_BEHRINGER, 0x00001604), + /* Behringer, Digital Mixer X32 series (X-UF Card) */ + SND_BEBOB_DEV_ENTRY(VEN_BEHRINGER, 0x00000006), + /* Apogee Electronics, Rosetta 200/400 (X-FireWire card) */ + /* Apogee Electronics, DA/AD/DD-16X (X-FireWire card) */ + SND_BEBOB_DEV_ENTRY(VEN_APOGEE, 0x00010048), + /* Apogee Electronics, Ensemble */ + SND_BEBOB_DEV_ENTRY(VEN_APOGEE, 0x00001eee), + /* ESI, Quatafire610 */ + SND_BEBOB_DEV_ENTRY(VEN_ESI, 0x00010064), + /* AcousticReality, eARMasterOne */ + SND_BEBOB_DEV_ENTRY(VEN_ACOUSTIC, 0x00000002), + /* CME, MatrixKFW */ + SND_BEBOB_DEV_ENTRY(VEN_CME, 0x00030000), + /* Phonic, Helix Board 12 MkII */ + SND_BEBOB_DEV_ENTRY(VEN_PHONIC, 0x00050000), + /* Phonic, Helix Board 18 MkII */ + SND_BEBOB_DEV_ENTRY(VEN_PHONIC, 0x00060000), + /* Phonic, Helix Board 24 MkII */ + SND_BEBOB_DEV_ENTRY(VEN_PHONIC, 0x00070000), + /* Phonic, Helix Board 12 Universal/18 Universal/24 Universal */ + SND_BEBOB_DEV_ENTRY(VEN_PHONIC, 0x00000000), + /* Lynx, Aurora 8/16 (LT-FW) */ + SND_BEBOB_DEV_ENTRY(VEN_LYNX, 0x00000001), + /* ICON, FireXon */ + SND_BEBOB_DEV_ENTRY(VEN_ICON, 0x00000001), + /* PrismSound, Orpheus */ + SND_BEBOB_DEV_ENTRY(VEN_PRISMSOUND, 0x00010048), + /* PrismSound, ADA-8XR */ + SND_BEBOB_DEV_ENTRY(VEN_PRISMSOUND, 0x0000ada8), + /* IDs are unknown but able to be supported */ + /* Apogee, Mini-ME Firewire */ + /* Apogee, Mini-DAC Firewire */ + /* Behringer, F-Control Audio 1616 */ + /* Behringer, F-Control Audio 610 */ + /* Cakawalk, Sonar Power Studio 66 */ + /* CME, UF400e */ + /* ESI, Quotafire XL */ + /* Infrasonic, DewX */ + /* Infrasonic, Windy6 */ + /* Mackie, Digital X Bus x.200 */ + /* Mackie, Digital X Bus x.400 */ + /* Phonic, HB 12 */ + /* Phonic, HB 24 */ + /* Phonic, HB 18 */ + /* Phonic, FireFly 202 */ + /* Phonic, FireFly 302 */ + /* Rolf Spuler, Firewire Guitar */ + {} +}; +MODULE_DEVICE_TABLE(ieee1394, bebob_id_table); + +static struct fw_driver bebob_driver = { + .driver = { + .owner = THIS_MODULE, + .name = "snd-bebob", + .bus = &fw_bus_type, + }, + .probe = bebob_probe, + .update = bebob_update, + .remove = bebob_remove, + .id_table = bebob_id_table, +}; + +static int __init +snd_bebob_init(void) +{ + return driver_register(&bebob_driver.driver); +} + +static void __exit +snd_bebob_exit(void) +{ + driver_unregister(&bebob_driver.driver); + mutex_destroy(&devices_mutex); +} + +module_init(snd_bebob_init); +module_exit(snd_bebob_exit); diff --git a/sound/firewire/bebob/bebob.h b/sound/firewire/bebob/bebob.h new file mode 100644 index 0000000..173f4558 --- /dev/null +++ b/sound/firewire/bebob/bebob.h @@ -0,0 +1,64 @@ +/* + * bebob.h - a part of driver for BeBoB based devices + * + * Copyright (c) 2013 Takashi Sakamoto + * + * Licensed under the terms of the GNU General Public License, version 2. + */ + +#ifndef SOUND_BEBOB_H_INCLUDED +#define SOUND_BEBOB_H_INCLUDED + +#include <linux/compat.h> +#include <linux/device.h> +#include <linux/firewire.h> +#include <linux/firewire-constants.h> +#include <linux/module.h> +#include <linux/mod_devicetable.h> +#include <linux/delay.h> +#include <linux/slab.h> + +#include <sound/core.h> +#include <sound/initval.h> + +#include "../lib.h" +#include "../fcp.h" + +/* basic register addresses on DM1000 */ +#define BEBOB_ADDR_REG_INFO 0xffffc8020000 +#define BEBOB_ADDR_REG_REQ 0xffffc8021000 + +struct snd_bebob { + struct snd_card *card; + struct fw_unit *unit; + int card_index; + + struct mutex mutex; + spinlock_t lock; +}; + +static inline int +snd_bebob_read_block(struct fw_unit *unit, u64 addr, void *buf, int size) +{ + return snd_fw_transaction(unit, TCODE_READ_BLOCK_REQUEST, + BEBOB_ADDR_REG_INFO + addr, + buf, size, 0); +} + +static inline int +snd_bebob_read_quad(struct fw_unit *unit, u64 addr, u32 *buf) +{ + return snd_fw_transaction(unit, TCODE_READ_QUADLET_REQUEST, + BEBOB_ADDR_REG_INFO + addr, + (void *)buf, sizeof(u32), 0); +} + +#define SND_BEBOB_DEV_ENTRY(vendor, model) \ +{ \ + .match_flags = IEEE1394_MATCH_VENDOR_ID | \ + IEEE1394_MATCH_MODEL_ID, \ + .vendor_id = vendor, \ + .model_id = model, \ +} + +#endif -- 1.8.3.2 |
From: Takashi S. <o-t...@sa...> - 2014-01-29 13:45:57
|
This commit adds proc interface to get information for debugging. - firmware information - stream formation - current clock source and sampling rate Signed-off-by: Takashi Sakamoto <o-t...@sa...> --- sound/firewire/bebob/Makefile | 2 +- sound/firewire/bebob/bebob.c | 2 + sound/firewire/bebob/bebob.h | 3 + sound/firewire/bebob/bebob_proc.c | 136 ++++++++++++++++++++++++++++++++++++++ 4 files changed, 142 insertions(+), 1 deletion(-) create mode 100644 sound/firewire/bebob/bebob_proc.c diff --git a/sound/firewire/bebob/Makefile b/sound/firewire/bebob/Makefile index 5cece62..757c40e 100644 --- a/sound/firewire/bebob/Makefile +++ b/sound/firewire/bebob/Makefile @@ -1,2 +1,2 @@ -snd-bebob-objs := bebob_command.o bebob_stream.o bebob.o +snd-bebob-objs := bebob_command.o bebob_stream.o bebob_proc.o bebob.o obj-m += snd-bebob.o diff --git a/sound/firewire/bebob/bebob.c b/sound/firewire/bebob/bebob.c index 273ef39..b19d7c5 100644 --- a/sound/firewire/bebob/bebob.c +++ b/sound/firewire/bebob/bebob.c @@ -167,6 +167,8 @@ bebob_probe(struct fw_unit *unit, if (err < 0) goto error; + snd_bebob_proc_init(bebob); + snd_card_set_dev(card, &unit->device); err = snd_card_register(card); if (err < 0) { diff --git a/sound/firewire/bebob/bebob.h b/sound/firewire/bebob/bebob.h index 0dce92b..e513fd3 100644 --- a/sound/firewire/bebob/bebob.h +++ b/sound/firewire/bebob/bebob.h @@ -20,6 +20,7 @@ #include <sound/core.h> #include <sound/initval.h> +#include <sound/info.h> #include "../lib.h" #include "../fcp.h" @@ -175,6 +176,8 @@ int snd_bebob_stream_stop_duplex(struct snd_bebob *bebob); void snd_bebob_stream_update_duplex(struct snd_bebob *bebob); void snd_bebob_stream_destroy_duplex(struct snd_bebob *bebob); +void snd_bebob_proc_init(struct snd_bebob *bebob); + #define SND_BEBOB_DEV_ENTRY(vendor, model) \ { \ .match_flags = IEEE1394_MATCH_VENDOR_ID | \ diff --git a/sound/firewire/bebob/bebob_proc.c b/sound/firewire/bebob/bebob_proc.c new file mode 100644 index 0000000..40099a5 --- /dev/null +++ b/sound/firewire/bebob/bebob_proc.c @@ -0,0 +1,136 @@ +/* + * bebob_proc.c - a part of driver for BeBoB based devices + * + * Copyright (c) 2013 Takashi Sakamoto + * + * Licensed under the terms of the GNU General Public License, version 2. + */ + +#include "./bebob.h" + +/* contents of information register */ +struct hw_info { + u64 manufacturer; + u32 protocol_ver; + u32 bld_ver; + u32 guid[2]; + u32 model_id; + u32 model_rev; + u64 fw_date; + u64 fw_time; + u32 fw_id; + u32 fw_ver; + u32 base_addr; + u32 max_size; + u64 bld_date; + u64 bld_time; +/* may not used in product + u64 dbg_date; + u64 dbg_time; + u32 dbg_id; + u32 dbg_version; +*/ +} __packed; + +static void +proc_read_hw_info(struct snd_info_entry *entry, + struct snd_info_buffer *buffer) +{ + struct snd_bebob *bebob = entry->private_data; + struct hw_info *info; + int err; + + info = kzalloc(sizeof(struct hw_info), GFP_KERNEL); + if (info == NULL) + return; + + err = snd_bebob_read_block(bebob->unit, 0, + info, sizeof(struct hw_info)); + if (err < 0) + goto end; + + snd_iprintf(buffer, "Manufacturer:\t%.8s\n", + (char *)&info->manufacturer); + snd_iprintf(buffer, "Protocol Ver:\t%d\n", info->protocol_ver); + snd_iprintf(buffer, "Build Ver:\t%d\n", info->bld_ver); + snd_iprintf(buffer, "GUID:\t\t0x%.8X%.8X\n", + info->guid[0], info->guid[1]); + snd_iprintf(buffer, "Model ID:\t0x%02X\n", info->model_id); + snd_iprintf(buffer, "Model Rev:\t%d\n", info->model_rev); + snd_iprintf(buffer, "Firmware Date:\t%.8s\n", (char *)&info->fw_date); + snd_iprintf(buffer, "Firmware Time:\t%.8s\n", (char *)&info->fw_time); + snd_iprintf(buffer, "Firmware ID:\t0x%X\n", info->fw_id); + snd_iprintf(buffer, "Firmware Ver:\t%d\n", info->fw_ver); + snd_iprintf(buffer, "Base Addr:\t0x%X\n", info->base_addr); + snd_iprintf(buffer, "Max Size:\t%d\n", info->max_size); + snd_iprintf(buffer, "Loader Date:\t%.8s\n", (char *)&info->bld_date); + snd_iprintf(buffer, "Loader Time:\t%.8s\n", (char *)&info->bld_time); + +end: + kfree(info); + return; +} + +static void +proc_read_formation(struct snd_info_entry *entry, + struct snd_info_buffer *buffer) +{ + struct snd_bebob *bebob = entry->private_data; + struct snd_bebob_stream_formation *formation; + unsigned int i; + + snd_iprintf(buffer, "Output Stream from device:\n"); + snd_iprintf(buffer, "\tRate\tPCM\tMIDI\n"); + formation = bebob->tx_stream_formations; + for (i = 0; i < SND_BEBOB_STRM_FMT_ENTRIES; i++) { + snd_iprintf(buffer, + "\t%d\t%d\t%d\n", snd_bebob_rate_table[i], + formation[i].pcm, formation[i].midi); + } + + snd_iprintf(buffer, "Input Stream to device:\n"); + snd_iprintf(buffer, "\tRate\tPCM\tMIDI\n"); + formation = bebob->rx_stream_formations; + for (i = 0; i < SND_BEBOB_STRM_FMT_ENTRIES; i++) { + snd_iprintf(buffer, + "\t%d\t%d\t%d\n", snd_bebob_rate_table[i], + formation[i].pcm, formation[i].midi); + } + + return; +} + +static void +proc_read_clock(struct snd_info_entry *entry, + struct snd_info_buffer *buffer) +{ + struct snd_bebob *bebob = entry->private_data; + unsigned int rate; + bool internal; + + if (snd_bebob_stream_get_rate(bebob, &rate) < 0) + return; + if (snd_bebob_stream_check_internal_clock(bebob, &internal) < 0) + return; + + snd_iprintf(buffer, "Clock Source: %s (MSU-dest: %d)", + (internal) ? "Internal" : "External", + bebob->sync_input_plug); + snd_iprintf(buffer, "Sampling rate: %d\n", rate); +} + +void snd_bebob_proc_init(struct snd_bebob *bebob) +{ + struct snd_info_entry *entry; + + if (!snd_card_proc_new(bebob->card, "#firmware", &entry)) + snd_info_set_text_ops(entry, bebob, proc_read_hw_info); + + if (!snd_card_proc_new(bebob->card, "#formation", &entry)) + snd_info_set_text_ops(entry, bebob, proc_read_formation); + + if (!snd_card_proc_new(bebob->card, "#clock", &entry)) + snd_info_set_text_ops(entry, bebob, proc_read_clock); + + return; +} -- 1.8.3.2 |
From: Takashi S. <o-t...@sa...> - 2014-01-29 13:46:00
|
This commit adds a functionality to capture/playback PCM samples. When AMDTP stream is already running for PCM or the source of clock is not internal, available sampling rate is limited at current one. Signed-off-by: Takashi Sakamoto <o-t...@sa...> --- sound/firewire/Kconfig | 1 + sound/firewire/bebob/Makefile | 1 + sound/firewire/bebob/bebob.c | 4 + sound/firewire/bebob/bebob.h | 4 + sound/firewire/bebob/bebob_pcm.c | 410 +++++++++++++++++++++++++++++++++++++++ 5 files changed, 420 insertions(+) create mode 100644 sound/firewire/bebob/bebob_pcm.c diff --git a/sound/firewire/Kconfig b/sound/firewire/Kconfig index 8081ca1..022fee0 100644 --- a/sound/firewire/Kconfig +++ b/sound/firewire/Kconfig @@ -83,6 +83,7 @@ config SND_BEBOB tristate "BridgeCo DM1000/1500 with BeBoB firmware" select SND_FIREWIRE_LIB select SND_RAWMIDI + select SND_PCM help Say Y here to include support for FireWire devices based on BridgeCo DM1000/1500 with BeBoB firmware: diff --git a/sound/firewire/bebob/Makefile b/sound/firewire/bebob/Makefile index 1e39e59..533718a 100644 --- a/sound/firewire/bebob/Makefile +++ b/sound/firewire/bebob/Makefile @@ -1,3 +1,4 @@ snd-bebob-objs := bebob_command.o bebob_stream.o bebob_proc.o bebob_midi.o \ + bebob_pcm.o \ bebob.o obj-m += snd-bebob.o diff --git a/sound/firewire/bebob/bebob.c b/sound/firewire/bebob/bebob.c index adb5493..a56917d 100644 --- a/sound/firewire/bebob/bebob.c +++ b/sound/firewire/bebob/bebob.c @@ -176,6 +176,10 @@ bebob_probe(struct fw_unit *unit, goto error; } + err = snd_bebob_create_pcm_devices(bebob); + if (err < 0) + goto error; + snd_card_set_dev(card, &unit->device); err = snd_card_register(card); if (err < 0) { diff --git a/sound/firewire/bebob/bebob.h b/sound/firewire/bebob/bebob.h index c8e774b..ddd31a2 100644 --- a/sound/firewire/bebob/bebob.h +++ b/sound/firewire/bebob/bebob.h @@ -22,6 +22,8 @@ #include <sound/initval.h> #include <sound/info.h> #include <sound/rawmidi.h> +#include <sound/pcm.h> +#include <sound/pcm_params.h> #include "../lib.h" #include "../fcp.h" @@ -181,6 +183,8 @@ void snd_bebob_proc_init(struct snd_bebob *bebob); int snd_bebob_create_midi_devices(struct snd_bebob *bebob); +int snd_bebob_create_pcm_devices(struct snd_bebob *bebob); + #define SND_BEBOB_DEV_ENTRY(vendor, model) \ { \ .match_flags = IEEE1394_MATCH_VENDOR_ID | \ diff --git a/sound/firewire/bebob/bebob_pcm.c b/sound/firewire/bebob/bebob_pcm.c new file mode 100644 index 0000000..e4c6c11 --- /dev/null +++ b/sound/firewire/bebob/bebob_pcm.c @@ -0,0 +1,410 @@ +/* + * bebob_pcm.c - a part of driver for BeBoB based devices + * + * Copyright (c) 2013 Takashi Sakamoto + * + * Licensed under the terms of the GNU General Public License, version 2. + */ + +#include "./bebob.h" + +static int +hw_rule_rate(struct snd_pcm_hw_params *params, struct snd_pcm_hw_rule *rule, + struct snd_bebob *bebob, + struct snd_bebob_stream_formation *formations) +{ + struct snd_interval *r = + hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE); + const struct snd_interval *c = + hw_param_interval_c(params, SNDRV_PCM_HW_PARAM_CHANNELS); + struct snd_interval t = { + .min = UINT_MAX, .max = 0, .integer = 1 + }; + unsigned int i; + + for (i = 0; i < SND_BEBOB_STRM_FMT_ENTRIES; i++) { + /* entry is invalid */ + if (formations[i].pcm == 0) + continue; + + if (!snd_interval_test(c, formations[i].pcm)) + continue; + + t.min = min(t.min, snd_bebob_rate_table[i]); + t.max = max(t.max, snd_bebob_rate_table[i]); + + } + return snd_interval_refine(r, &t); +} + +static int +hw_rule_channels(struct snd_pcm_hw_params *params, struct snd_pcm_hw_rule *rule, + struct snd_bebob *bebob, + struct snd_bebob_stream_formation *formations) +{ + struct snd_interval *c = + hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS); + const struct snd_interval *r = + hw_param_interval_c(params, SNDRV_PCM_HW_PARAM_RATE); + struct snd_interval t = { + .min = UINT_MAX, .max = 0, .integer = 1 + }; + + unsigned int i; + + for (i = 0; i < SND_BEBOB_STRM_FMT_ENTRIES; i++) { + /* entry is invalid */ + if (formations[i].pcm == 0) + continue; + + if (!snd_interval_test(r, snd_bebob_rate_table[i])) + continue; + + t.min = min(t.min, formations[i].pcm); + t.max = max(t.max, formations[i].pcm); + } + + return snd_interval_refine(c, &t); +} + +static inline int +hw_rule_capture_rate(struct snd_pcm_hw_params *params, + struct snd_pcm_hw_rule *rule) +{ + struct snd_bebob *bebob = rule->private; + return hw_rule_rate(params, rule, bebob, + bebob->tx_stream_formations); +} + +static inline int +hw_rule_playback_rate(struct snd_pcm_hw_params *params, + struct snd_pcm_hw_rule *rule) +{ + struct snd_bebob *bebob = rule->private; + return hw_rule_rate(params, rule, bebob, + bebob->rx_stream_formations); +} + +static inline int +hw_rule_capture_channels(struct snd_pcm_hw_params *params, + struct snd_pcm_hw_rule *rule) +{ + struct snd_bebob *bebob = rule->private; + return hw_rule_channels(params, rule, bebob, + bebob->tx_stream_formations); +} + +static inline int +hw_rule_playback_channels(struct snd_pcm_hw_params *params, + struct snd_pcm_hw_rule *rule) +{ + struct snd_bebob *bebob = rule->private; + return hw_rule_channels(params, rule, bebob, + bebob->rx_stream_formations); +} + +static void +prepare_channels(struct snd_pcm_hardware *hw, + struct snd_bebob_stream_formation *formations) +{ + unsigned int i; + + for (i = 0; i < SND_BEBOB_STRM_FMT_ENTRIES; i++) { + /* entry has no PCM channels */ + if (formations[i].pcm == 0) + continue; + + hw->channels_min = min(hw->channels_min, formations[i].pcm); + hw->channels_max = max(hw->channels_max, formations[i].pcm); + } + + return; +} + +static void +prepare_rates(struct snd_pcm_hardware *hw, + struct snd_bebob_stream_formation *formations) +{ + unsigned int i; + + for (i = 0; i < SND_BEBOB_STRM_FMT_ENTRIES; i++) { + /* entry has no PCM channels */ + if (formations[i].pcm == 0) + continue; + + hw->rate_min = min(hw->rate_min, snd_bebob_rate_table[i]); + hw->rate_max = max(hw->rate_max, snd_bebob_rate_table[i]); + hw->rates |= snd_pcm_rate_to_rate_bit(snd_bebob_rate_table[i]); + } + + return; +} + +static int +pcm_init_hw_params(struct snd_bebob *bebob, + struct snd_pcm_substream *substream) +{ + int err; + + static const struct snd_pcm_hardware hw = { + .info = SNDRV_PCM_INFO_MMAP | + SNDRV_PCM_INFO_BATCH | + SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_JOINT_DUPLEX | + /* for Open Sound System compatibility */ + SNDRV_PCM_INFO_MMAP_VALID | + SNDRV_PCM_INFO_BLOCK_TRANSFER, + /* set up later */ + .rates = 0, + .rate_min = UINT_MAX, + .rate_max = 0, + /* set up later */ + .channels_min = UINT_MAX, + .channels_max = 0, + .buffer_bytes_max = 4 * 16 * 2048, + .period_bytes_min = 1, + .period_bytes_max = 4 * 16 * 2048, + .periods_min = 1, + .periods_max = UINT_MAX, + }; + + substream->runtime->hw = hw; + + /* add rule between channels and sampling rate */ + if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) { + prepare_rates(&substream->runtime->hw, + bebob->tx_stream_formations); + prepare_channels(&substream->runtime->hw, + bebob->tx_stream_formations); + substream->runtime->hw.formats = SNDRV_PCM_FMTBIT_S32_LE; + snd_pcm_hw_rule_add(substream->runtime, 0, + SNDRV_PCM_HW_PARAM_CHANNELS, + hw_rule_capture_channels, bebob, + SNDRV_PCM_HW_PARAM_RATE, -1); + snd_pcm_hw_rule_add(substream->runtime, 0, + SNDRV_PCM_HW_PARAM_RATE, + hw_rule_capture_rate, bebob, + SNDRV_PCM_HW_PARAM_CHANNELS, -1); + } else { + prepare_rates(&substream->runtime->hw, + bebob->rx_stream_formations); + prepare_channels(&substream->runtime->hw, + bebob->rx_stream_formations); + substream->runtime->hw.formats = AMDTP_OUT_PCM_FORMAT_BITS; + snd_pcm_hw_rule_add(substream->runtime, 0, + SNDRV_PCM_HW_PARAM_CHANNELS, + hw_rule_playback_channels, bebob, + SNDRV_PCM_HW_PARAM_RATE, -1); + snd_pcm_hw_rule_add(substream->runtime, 0, + SNDRV_PCM_HW_PARAM_RATE, + hw_rule_playback_rate, bebob, + SNDRV_PCM_HW_PARAM_CHANNELS, -1); + } + + /* AM824 in IEC 61883-6 can deliver 24bit data */ + err = snd_pcm_hw_constraint_msbits(substream->runtime, 0, 32, 24); + if (err < 0) + goto end; + + /* + * AMDTP functionality in firewire-lib require periods to be aligned to + * 16 bit, or 24bit inner 32bit. + */ + err = snd_pcm_hw_constraint_step(substream->runtime, 0, + SNDRV_PCM_HW_PARAM_PERIOD_BYTES, 32); + if (err < 0) + goto end; + err = snd_pcm_hw_constraint_step(substream->runtime, 0, + SNDRV_PCM_HW_PARAM_BUFFER_BYTES, 32); +end: + return err; +} + +static int +pcm_open(struct snd_pcm_substream *substream) +{ + struct snd_bebob *bebob = substream->private_data; + unsigned int sampling_rate; + bool internal; + int err; + + err = pcm_init_hw_params(bebob, substream); + if (err < 0) + goto end; + + err = snd_bebob_stream_check_internal_clock(bebob, &internal); + if (err < 0) + goto end; + + /* + * When source of clock is internal or any PCM stream are running, + * the available sampling rate is limited at current sampling rate. + */ + if (!internal || + amdtp_stream_pcm_running(&bebob->tx_stream) || + amdtp_stream_pcm_running(&bebob->rx_stream)) { + err = snd_bebob_stream_get_rate(bebob, &sampling_rate); + if (err < 0) + goto end; + + substream->runtime->hw.rate_min = sampling_rate; + substream->runtime->hw.rate_max = sampling_rate; + } + + snd_pcm_set_sync(substream); + +end: + return err; +} + +static int +pcm_close(struct snd_pcm_substream *substream) +{ + return 0; +} + +static int +pcm_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *hw_params) +{ + return snd_pcm_lib_alloc_vmalloc_buffer(substream, + params_buffer_bytes(hw_params)); +} + +static int +pcm_hw_free(struct snd_pcm_substream *substream) +{ + struct snd_bebob *bebob = substream->private_data; + + snd_bebob_stream_stop_duplex(bebob); + + return snd_pcm_lib_free_vmalloc_buffer(substream); +} + +static int +pcm_capture_prepare(struct snd_pcm_substream *substream) +{ + struct snd_bebob *bebob = substream->private_data; + struct snd_pcm_runtime *runtime = substream->runtime; + int err; + + err = snd_bebob_stream_start_duplex(bebob, &bebob->tx_stream, + runtime->rate); + if (err < 0) + goto end; + + amdtp_stream_set_pcm_format(&bebob->tx_stream, runtime->format); + amdtp_stream_pcm_prepare(&bebob->tx_stream); +end: + return err; +} +static int +pcm_playback_prepare(struct snd_pcm_substream *substream) +{ + struct snd_bebob *bebob = substream->private_data; + struct snd_pcm_runtime *runtime = substream->runtime; + int err; + + err = snd_bebob_stream_start_duplex(bebob, &bebob->rx_stream, + runtime->rate); + if (err < 0) + goto end; + + amdtp_stream_set_pcm_format(&bebob->rx_stream, runtime->format); + amdtp_stream_pcm_prepare(&bebob->rx_stream); +end: + return err; +} + +static int +pcm_capture_trigger(struct snd_pcm_substream *substream, int cmd) +{ + struct snd_bebob *bebob = substream->private_data; + + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + amdtp_stream_pcm_trigger(&bebob->tx_stream, substream); + break; + case SNDRV_PCM_TRIGGER_STOP: + amdtp_stream_pcm_trigger(&bebob->tx_stream, NULL); + break; + default: + return -EINVAL; + } + + return 0; +} +static int +pcm_playback_trigger(struct snd_pcm_substream *substream, int cmd) +{ + struct snd_bebob *bebob = substream->private_data; + + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + amdtp_stream_pcm_trigger(&bebob->rx_stream, substream); + break; + case SNDRV_PCM_TRIGGER_STOP: + amdtp_stream_pcm_trigger(&bebob->rx_stream, NULL); + break; + default: + return -EINVAL; + } + + return 0; +} + +static snd_pcm_uframes_t +pcm_capture_pointer(struct snd_pcm_substream *sbstrm) +{ + struct snd_bebob *bebob = sbstrm->private_data; + return amdtp_stream_pcm_pointer(&bebob->tx_stream); +} +static snd_pcm_uframes_t +pcm_playback_pointer(struct snd_pcm_substream *sbstrm) +{ + struct snd_bebob *bebob = sbstrm->private_data; + return amdtp_stream_pcm_pointer(&bebob->rx_stream); +} + +static struct snd_pcm_ops pcm_capture_ops = { + .open = pcm_open, + .close = pcm_close, + .ioctl = snd_pcm_lib_ioctl, + .hw_params = pcm_hw_params, + .hw_free = pcm_hw_free, + .prepare = pcm_capture_prepare, + .trigger = pcm_capture_trigger, + .pointer = pcm_capture_pointer, + .page = snd_pcm_lib_get_vmalloc_page, +}; +static struct snd_pcm_ops pcm_playback_ops = { + .open = pcm_open, + .close = pcm_close, + .ioctl = snd_pcm_lib_ioctl, + .hw_params = pcm_hw_params, + .hw_free = pcm_hw_free, + .prepare = pcm_playback_prepare, + .trigger = pcm_playback_trigger, + .pointer = pcm_playback_pointer, + .page = snd_pcm_lib_get_vmalloc_page, + .mmap = snd_pcm_lib_mmap_vmalloc, +}; + +int snd_bebob_create_pcm_devices(struct snd_bebob *bebob) +{ + struct snd_pcm *pcm; + int err; + + err = snd_pcm_new(bebob->card, bebob->card->driver, 0, 1, 1, &pcm); + if (err < 0) + goto end; + + pcm->private_data = bebob; + snprintf(pcm->name, sizeof(pcm->name), + "%s PCM", bebob->card->shortname); + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &pcm_playback_ops); + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &pcm_capture_ops); + +end: + return err; +} -- 1.8.3.2 |
From: Takashi S. <o-t...@sa...> - 2014-01-29 13:46:04
|
This commit is for some devices which have its own operations or quirks. Many functionality should be implemented in user land. Then this commit adds functionality related to stream such as sampling frequency or clock source. For help to debug, this commit adds the functionality to get metering information if it's available. To help these functionalities, this commit adds some AV/C commands defined in 'AV/C Audio Subunit Specification (1394TA). Signed-off-by: Takashi Sakamoto <o-t...@sa...> --- sound/firewire/bebob/Makefile | 3 +- sound/firewire/bebob/bebob.c | 72 ++++++++++++++++++++------------ sound/firewire/bebob/bebob.h | 33 ++++++++++++++- sound/firewire/bebob/bebob_command.c | 79 ++++++++++++++++++++++++++++++++++++ sound/firewire/bebob/bebob_pcm.c | 3 +- sound/firewire/bebob/bebob_proc.c | 61 +++++++++++++++++++++++++--- sound/firewire/bebob/bebob_stream.c | 26 +++++++++--- 7 files changed, 235 insertions(+), 42 deletions(-) diff --git a/sound/firewire/bebob/Makefile b/sound/firewire/bebob/Makefile index 7808777..e4b08e3 100644 --- a/sound/firewire/bebob/Makefile +++ b/sound/firewire/bebob/Makefile @@ -1,4 +1,3 @@ snd-bebob-objs := bebob_command.o bebob_stream.o bebob_proc.o bebob_midi.o \ - bebob_pcm.o bebob_hwdep.o \ - bebob.o + bebob_pcm.o bebob_hwdep.o bebob.o obj-m += snd-bebob.o diff --git a/sound/firewire/bebob/bebob.c b/sound/firewire/bebob/bebob.c index 19f2831..a53eb13 100644 --- a/sound/firewire/bebob/bebob.c +++ b/sound/firewire/bebob/bebob.c @@ -128,6 +128,7 @@ bebob_probe(struct fw_unit *unit, { struct snd_card *card; struct snd_bebob *bebob; + const struct snd_bebob_spec *spec; unsigned int card_index; int err; @@ -142,6 +143,12 @@ bebob_probe(struct fw_unit *unit, goto end; } + spec = (const struct snd_bebob_spec *)entry->driver_data; + if (spec == NULL) { + err = -ENOSYS; + goto end; + } + err = snd_card_create(index[card_index], id[card_index], THIS_MODULE, sizeof(struct snd_bebob), &card); if (err < 0) @@ -152,6 +159,7 @@ bebob_probe(struct fw_unit *unit, bebob->card = card; bebob->unit = unit; bebob->card_index = -1; + bebob->spec = spec; mutex_init(&bebob->mutex); spin_lock_init(&bebob->lock); init_waitqueue_head(&bebob->hwdep_wait); @@ -220,62 +228,72 @@ static void bebob_remove(struct fw_unit *unit) snd_card_free_when_closed(bebob->card); } +struct snd_bebob_rate_spec normal_rate_spec = { + .get = &snd_bebob_stream_get_rate, + .set = &snd_bebob_stream_set_rate +}; +static const struct snd_bebob_spec spec_normal = { + .clock = NULL, + .rate = &normal_rate_spec, + .meter = NULL +}; + static const struct ieee1394_device_id bebob_id_table[] = { /* Edirol, FA-66 */ - SND_BEBOB_DEV_ENTRY(VEN_EDIROL, 0x00010049), + SND_BEBOB_DEV_ENTRY(VEN_EDIROL, 0x00010049, &spec_normal), /* Edirol, FA-101 */ - SND_BEBOB_DEV_ENTRY(VEN_EDIROL, 0x00010048), + SND_BEBOB_DEV_ENTRY(VEN_EDIROL, 0x00010048, &spec_normal), /* Presonus, FIREBOX */ - SND_BEBOB_DEV_ENTRY(VEN_PRESONUS, 0x00010000), + SND_BEBOB_DEV_ENTRY(VEN_PRESONUS, 0x00010000, &spec_normal), /* PreSonus, FIREPOD/FP10 */ - SND_BEBOB_DEV_ENTRY(VEN_PRESONUS, 0x00010066), + SND_BEBOB_DEV_ENTRY(VEN_PRESONUS, 0x00010066, &spec_normal), /* PreSonus, Inspire1394 */ - SND_BEBOB_DEV_ENTRY(VEN_PRESONUS, 0x00010001), + SND_BEBOB_DEV_ENTRY(VEN_PRESONUS, 0x00010001, &spec_normal), /* BridgeCo, RDAudio1 */ - SND_BEBOB_DEV_ENTRY(VEN_BRIDGECO, 0x00010048), + SND_BEBOB_DEV_ENTRY(VEN_BRIDGECO, 0x00010048, &spec_normal), /* BridgeCo, Audio5 */ - SND_BEBOB_DEV_ENTRY(VEN_BRIDGECO, 0x00010049), + SND_BEBOB_DEV_ENTRY(VEN_BRIDGECO, 0x00010049, &spec_normal), /* Mackie, Onyx 1220/1620/1640 (Firewire I/O Card) */ - SND_BEBOB_DEV_ENTRY(VEN_MACKIE, 0x00010065), + SND_BEBOB_DEV_ENTRY(VEN_MACKIE, 0x00010065, &spec_normal), /* Mackie, d.2 (Firewire Option) */ - SND_BEBOB_DEV_ENTRY(VEN_MACKIE, 0x00010067), + SND_BEBOB_DEV_ENTRY(VEN_MACKIE, 0x00010067, &spec_normal), /* Stanton, ScratchAmp */ - SND_BEBOB_DEV_ENTRY(VEN_STANTON, 0x00000001), + SND_BEBOB_DEV_ENTRY(VEN_STANTON, 0x00000001, &spec_normal), /* Tascam, IF-FW DM */ - SND_BEBOB_DEV_ENTRY(VEN_TASCAM, 0x00010067), + SND_BEBOB_DEV_ENTRY(VEN_TASCAM, 0x00010067, &spec_normal), /* Behringer, XENIX UFX 1204 */ - SND_BEBOB_DEV_ENTRY(VEN_BEHRINGER, 0x00001204), + SND_BEBOB_DEV_ENTRY(VEN_BEHRINGER, 0x00001204, &spec_normal), /* Behringer, XENIX UFX 1604 */ - SND_BEBOB_DEV_ENTRY(VEN_BEHRINGER, 0x00001604), + SND_BEBOB_DEV_ENTRY(VEN_BEHRINGER, 0x00001604, &spec_normal), /* Behringer, Digital Mixer X32 series (X-UF Card) */ - SND_BEBOB_DEV_ENTRY(VEN_BEHRINGER, 0x00000006), + SND_BEBOB_DEV_ENTRY(VEN_BEHRINGER, 0x00000006, &spec_normal), /* Apogee Electronics, Rosetta 200/400 (X-FireWire card) */ /* Apogee Electronics, DA/AD/DD-16X (X-FireWire card) */ - SND_BEBOB_DEV_ENTRY(VEN_APOGEE, 0x00010048), + SND_BEBOB_DEV_ENTRY(VEN_APOGEE, 0x00010048, &spec_normal), /* Apogee Electronics, Ensemble */ - SND_BEBOB_DEV_ENTRY(VEN_APOGEE, 0x00001eee), + SND_BEBOB_DEV_ENTRY(VEN_APOGEE, 0x00001eee, &spec_normal), /* ESI, Quatafire610 */ - SND_BEBOB_DEV_ENTRY(VEN_ESI, 0x00010064), + SND_BEBOB_DEV_ENTRY(VEN_ESI, 0x00010064, &spec_normal), /* AcousticReality, eARMasterOne */ - SND_BEBOB_DEV_ENTRY(VEN_ACOUSTIC, 0x00000002), + SND_BEBOB_DEV_ENTRY(VEN_ACOUSTIC, 0x00000002, &spec_normal), /* CME, MatrixKFW */ - SND_BEBOB_DEV_ENTRY(VEN_CME, 0x00030000), + SND_BEBOB_DEV_ENTRY(VEN_CME, 0x00030000, &spec_normal), /* Phonic, Helix Board 12 MkII */ - SND_BEBOB_DEV_ENTRY(VEN_PHONIC, 0x00050000), + SND_BEBOB_DEV_ENTRY(VEN_PHONIC, 0x00050000, &spec_normal), /* Phonic, Helix Board 18 MkII */ - SND_BEBOB_DEV_ENTRY(VEN_PHONIC, 0x00060000), + SND_BEBOB_DEV_ENTRY(VEN_PHONIC, 0x00060000, &spec_normal), /* Phonic, Helix Board 24 MkII */ - SND_BEBOB_DEV_ENTRY(VEN_PHONIC, 0x00070000), + SND_BEBOB_DEV_ENTRY(VEN_PHONIC, 0x00070000, &spec_normal), /* Phonic, Helix Board 12 Universal/18 Universal/24 Universal */ - SND_BEBOB_DEV_ENTRY(VEN_PHONIC, 0x00000000), + SND_BEBOB_DEV_ENTRY(VEN_PHONIC, 0x00000000, &spec_normal), /* Lynx, Aurora 8/16 (LT-FW) */ - SND_BEBOB_DEV_ENTRY(VEN_LYNX, 0x00000001), + SND_BEBOB_DEV_ENTRY(VEN_LYNX, 0x00000001, &spec_normal), /* ICON, FireXon */ - SND_BEBOB_DEV_ENTRY(VEN_ICON, 0x00000001), + SND_BEBOB_DEV_ENTRY(VEN_ICON, 0x00000001, &spec_normal), /* PrismSound, Orpheus */ - SND_BEBOB_DEV_ENTRY(VEN_PRISMSOUND, 0x00010048), + SND_BEBOB_DEV_ENTRY(VEN_PRISMSOUND, 0x00010048, &spec_normal), /* PrismSound, ADA-8XR */ - SND_BEBOB_DEV_ENTRY(VEN_PRISMSOUND, 0x0000ada8), + SND_BEBOB_DEV_ENTRY(VEN_PRISMSOUND, 0x0000ada8, &spec_normal), /* IDs are unknown but able to be supported */ /* Apogee, Mini-ME Firewire */ /* Apogee, Mini-DAC Firewire */ diff --git a/sound/firewire/bebob/bebob.h b/sound/firewire/bebob/bebob.h index 57ad5e6..ea53d56 100644 --- a/sound/firewire/bebob/bebob.h +++ b/sound/firewire/bebob/bebob.h @@ -48,6 +48,28 @@ struct snd_bebob_stream_formation { /* this is a lookup table for index of stream formations */ extern const unsigned int snd_bebob_rate_table[SND_BEBOB_STRM_FMT_ENTRIES]; +/* device specific operations */ +#define SND_BEBOB_CLOCK_INTERNAL "Internal" +struct snd_bebob_clock_spec { + unsigned int num; + char **labels; + int (*get)(struct snd_bebob *bebob, unsigned int *id); +}; +struct snd_bebob_rate_spec { + int (*get)(struct snd_bebob *bebob, unsigned int *rate); + int (*set)(struct snd_bebob *bebob, unsigned int rate); +}; +struct snd_bebob_meter_spec { + unsigned int num; + char **labels; + int (*get)(struct snd_bebob *bebob, u32 *target, unsigned int size); +}; +struct snd_bebob_spec { + struct snd_bebob_clock_spec *clock; + struct snd_bebob_rate_spec *rate; + struct snd_bebob_meter_spec *meter; +}; + struct snd_bebob { struct snd_card *card; struct fw_unit *unit; @@ -56,6 +78,8 @@ struct snd_bebob { struct mutex mutex; spinlock_t lock; + const struct snd_bebob_spec *spec; + unsigned int midi_input_ports; unsigned int midi_output_ports; @@ -95,6 +119,12 @@ snd_bebob_read_quad(struct fw_unit *unit, u64 addr, u32 *buf) (void *)buf, sizeof(u32), 0); } +/* AV/C Audio Subunit Specification 1.0 (Oct 2000, 1394TA) */ +int avc_audio_set_selector(struct fw_unit *unit, unsigned int subunit_id, + unsigned int fb_id, unsigned int num); +int avc_audio_get_selector(struct fw_unit *unit, unsigned int subunit_id, + unsigned int fb_id, unsigned int *num); + /* * AVC command extensions, AV/C Unit and Subunit, Revision 17 * (Nov 2003, BridgeCo) @@ -198,12 +228,13 @@ int snd_bebob_create_pcm_devices(struct snd_bebob *bebob); int snd_bebob_create_hwdep_device(struct snd_bebob *bebob); -#define SND_BEBOB_DEV_ENTRY(vendor, model) \ +#define SND_BEBOB_DEV_ENTRY(vendor, model, data) \ { \ .match_flags = IEEE1394_MATCH_VENDOR_ID | \ IEEE1394_MATCH_MODEL_ID, \ .vendor_id = vendor, \ .model_id = model, \ + .driver_data = (kernel_ulong_t)data \ } #endif diff --git a/sound/firewire/bebob/bebob_command.c b/sound/firewire/bebob/bebob_command.c index 22a86d0..c63c6d8 100644 --- a/sound/firewire/bebob/bebob_command.c +++ b/sound/firewire/bebob/bebob_command.c @@ -11,6 +11,85 @@ #define BEBOB_COMMAND_MAX_TRIAL 3 #define BEBOB_COMMAND_WAIT_MSEC 100 +int avc_audio_set_selector(struct fw_unit *unit, unsigned int subunit_id, + unsigned int fb_id, unsigned int num) +{ + u8 *buf; + int err; + + buf = kzalloc(12, GFP_KERNEL); + if (buf == NULL) + return -ENOMEM; + + buf[0] = 0x00; /* AV/C CONTROL */ + buf[1] = 0x08 | (0x07 & subunit_id); /* AUDIO SUBUNIT ID */ + buf[2] = 0xb8; /* FUNCTION BLOCK */ + buf[3] = 0x80; /* type is 'selector'*/ + buf[4] = 0xff & fb_id; /* function block id */ + buf[5] = 0x10; /* control attribute is CURRENT */ + buf[6] = 0x02; /* selector length is 2 */ + buf[7] = 0xff & num; /* input function block plug number */ + buf[8] = 0x01; /* control selector is SELECTOR_CONTROL */ + + /* do transaction and check buf[1-8] are the same against command */ + err = fcp_avc_transaction(unit, buf, 12, buf, 12, + BIT(1) | BIT(2) | BIT(3) | BIT(4) | BIT(5) | + BIT(6) | BIT(7) | BIT(8)); + if (err < 0) + goto end; + if ((err < 6) || (buf[0] != 0x09)) { + dev_err(&unit->device, + "failed to set selector %d: 0x%02X\n", + fb_id, buf[0]); + err = -EIO; + goto end; + } +end: + kfree(buf); + return err; +} + +int avc_audio_get_selector(struct fw_unit *unit, unsigned int subunit_id, + unsigned int fb_id, unsigned int *num) +{ + u8 *buf; + int err; + + buf = kzalloc(12, GFP_KERNEL); + if (buf == NULL) + return -ENOMEM; + + buf[0] = 0x01; /* AV/C STATUS */ + buf[1] = 0x08 | (0x07 & subunit_id); /* AUDIO SUBUNIT ID */ + buf[2] = 0xb8; /* FUNCTION BLOCK */ + buf[3] = 0x80; /* type is 'selector'*/ + buf[4] = 0xff & fb_id; /* function block id */ + buf[5] = 0x10; /* control attribute is CURRENT */ + buf[6] = 0x02; /* selector length is 2 */ + buf[7] = 0xff; /* input function block plug number */ + buf[8] = 0x01; /* control selector is SELECTOR_CONTROL */ + + /* do transaction and check buf[1-6,8] are the same against command */ + err = fcp_avc_transaction(unit, buf, 12, buf, 12, + BIT(1) | BIT(2) | BIT(3) | BIT(4) | BIT(5) | + BIT(6) | BIT(8)); + if (err < 0) + goto end; + if ((err < 6) || (buf[0] != 0x0c)) { + dev_err(&unit->device, + "failed to get selector %d: 0x%02X\n", + fb_id, buf[0]); + err = -EIO; + goto end; + } + + *num = buf[7]; + err = 0; +end: + kfree(buf); + return err; +} + static inline void avc_bridgeco_fill_command_base(u8 *buf, unsigned int ctype, unsigned int opcode, unsigned int subfunction, diff --git a/sound/firewire/bebob/bebob_pcm.c b/sound/firewire/bebob/bebob_pcm.c index 70fa53a..8126efc 100644 --- a/sound/firewire/bebob/bebob_pcm.c +++ b/sound/firewire/bebob/bebob_pcm.c @@ -224,6 +224,7 @@ static int pcm_open(struct snd_pcm_substream *substream) { struct snd_bebob *bebob = substream->private_data; + struct snd_bebob_rate_spec *spec = bebob->spec->rate; unsigned int sampling_rate; bool internal; int err; @@ -247,7 +248,7 @@ pcm_open(struct snd_pcm_substream *substream) if (!internal || amdtp_stream_pcm_running(&bebob->tx_stream) || amdtp_stream_pcm_running(&bebob->rx_stream)) { - err = snd_bebob_stream_get_rate(bebob, &sampling_rate); + err = spec->get(bebob, &sampling_rate); if (err < 0) goto err_locked; diff --git a/sound/firewire/bebob/bebob_proc.c b/sound/firewire/bebob/bebob_proc.c index 40099a5..b8ad4b8 100644 --- a/sound/firewire/bebob/bebob_proc.c +++ b/sound/firewire/bebob/bebob_proc.c @@ -72,6 +72,42 @@ end: } static void +proc_read_meters(struct snd_info_entry *entry, + struct snd_info_buffer *buffer) +{ + struct snd_bebob *bebob = entry->private_data; + struct snd_bebob_meter_spec *spec = bebob->spec->meter; + u32 *buf; + unsigned int i, c, channels, size; + int err; + + if (spec == NULL) + return; + + channels = spec->num * 2; + size = channels * sizeof(u32); + buf = kmalloc(size, GFP_KERNEL); + if (buf == NULL) + return; + + err = spec->get(bebob, buf, size); + if (err < 0) + goto end; + + for (i = 0, c = 1; i < channels; i++) { + snd_iprintf(buffer, "%s %d:\t%d\n", + spec->labels[i / 2], c++, buf[i]); + if ((i + 1 < channels - 1) && + (strcmp(spec->labels[i / 2], + spec->labels[(i + 1) / 2]) != 0)) + c = 1; + } +end: + kfree(buf); + return; +} + +static void proc_read_formation(struct snd_info_entry *entry, struct snd_info_buffer *buffer) { @@ -105,18 +141,26 @@ proc_read_clock(struct snd_info_entry *entry, struct snd_info_buffer *buffer) { struct snd_bebob *bebob = entry->private_data; + struct snd_bebob_clock_spec *clk_spec = bebob->spec->clock; unsigned int rate; bool internal; + unsigned int id; if (snd_bebob_stream_get_rate(bebob, &rate) < 0) return; - if (snd_bebob_stream_check_internal_clock(bebob, &internal) < 0) - return; - - snd_iprintf(buffer, "Clock Source: %s (MSU-dest: %d)", - (internal) ? "Internal" : "External", - bebob->sync_input_plug); snd_iprintf(buffer, "Sampling rate: %d\n", rate); + + if (!clk_spec) { + if (snd_bebob_stream_check_internal_clock(bebob, &internal) < 0) + return; + snd_iprintf(buffer, "Clock Source: %s (MSU-dest: %d)\n", + (internal) ? "Internal" : "External", + bebob->sync_input_plug); + } else { + if (clk_spec->get(bebob, &id) < 0) + return; + snd_iprintf(buffer, "Clock Source: %s\n", clk_spec->labels[id]); + } } void snd_bebob_proc_init(struct snd_bebob *bebob) @@ -132,5 +176,10 @@ void snd_bebob_proc_init(struct snd_bebob *bebob) if (!snd_card_proc_new(bebob->card, "#clock", &entry)) snd_info_set_text_ops(entry, bebob, proc_read_clock); + if (bebob->spec->meter != NULL) { + if (!snd_card_proc_new(bebob->card, "#meter", &entry)) + snd_info_set_text_ops(entry, bebob, proc_read_meters); + } + return; } diff --git a/sound/firewire/bebob/bebob_stream.c b/sound/firewire/bebob/bebob_stream.c index 273843b..506eb6a8 100644 --- a/sound/firewire/bebob/bebob_stream.c +++ b/sound/firewire/bebob/bebob_stream.c @@ -107,13 +107,26 @@ end: int snd_bebob_stream_check_internal_clock(struct snd_bebob *bebob, bool *internal) { + struct snd_bebob_clock_spec *clk_spec = bebob->spec->clock; u8 addr[AVC_BRIDGECO_ADDR_BYTES], input[7]; + unsigned int id; int err = 0; *internal = false; + /* 1.The device has its own operation to switch source of clock */ + if (clk_spec) { + err = clk_spec->get(bebob, &id); + if (err < 0) + goto end; + if (strncmp(clk_spec->labels[id], SND_BEBOB_CLOCK_INTERNAL, + strlen(SND_BEBOB_CLOCK_INTERNAL)) == 0) + *internal = true; + goto end; + } + /* - * 1.The device don't support for switching source of clock + * 2.The device don't support for switching source of clock * then assumed to use internal clock always */ if (bebob->sync_input_plug < 0) { @@ -122,7 +135,7 @@ snd_bebob_stream_check_internal_clock(struct snd_bebob *bebob, bool *internal) } /* - * 2.The device supports to switch source of clock by an usual way. + * 3.The device supports to switch source of clock by an usual way. * Let's check input for 'Music Sub Unit Sync Input' plug. */ avc_bridgeco_fill_subunit_addr(addr, 0x60, AVC_BRIDGECO_PLUG_DIR_IN, @@ -398,6 +411,7 @@ int snd_bebob_stream_start_duplex(struct snd_bebob *bebob, struct amdtp_stream *request, unsigned int rate) { + struct snd_bebob_rate_spec *rate_spec = bebob->spec->rate; struct amdtp_stream *master, *slave; enum cip_flags sync_mode; unsigned int curr_rate; @@ -428,7 +442,7 @@ int snd_bebob_stream_start_duplex(struct snd_bebob *bebob, amdtp_stream_stop(master); /* stop streams if rate is different */ - err = snd_bebob_stream_get_rate(bebob, &curr_rate); + err = rate_spec->get(bebob, &curr_rate); if (err < 0) goto end; if (rate == 0) @@ -450,7 +464,7 @@ int snd_bebob_stream_start_duplex(struct snd_bebob *bebob, * If establishing connections at first, Yamaha GO46 * (and maybe Terratec X24) don't generate sound. */ - err = snd_bebob_stream_set_rate(bebob, rate); + err = rate_spec->set(bebob, rate); if (err < 0) goto end; @@ -709,6 +723,7 @@ end: int snd_bebob_stream_discover(struct snd_bebob *bebob) { u8 plugs[AVC_PLUG_INFO_BUF_COUNT], addr[AVC_BRIDGECO_ADDR_BYTES]; + struct snd_bebob_clock_spec *clk_spec = bebob->spec->clock; enum avc_bridgeco_plug_type type; unsigned int i; int err; @@ -782,7 +797,8 @@ int snd_bebob_stream_discover(struct snd_bebob *bebob) } /* for check source of clock later */ - err = seek_msu_sync_input_plug(bebob); + if (!clk_spec) + err = seek_msu_sync_input_plug(bebob); end: return err; } -- 1.8.3.2 |
From: Takashi S. <o-t...@sa...> - 2014-01-29 13:46:04
|
This commit adds a functionality to capture/playback MIDI messages. When no AMDTP streams are running, this module starts AMDTP stream at current sampling rate for MIDI stream. Signed-off-by: Takashi Sakamoto <o-t...@sa...> --- sound/firewire/Kconfig | 1 + sound/firewire/bebob/Makefile | 3 +- sound/firewire/bebob/bebob.c | 7 ++ sound/firewire/bebob/bebob.h | 3 + sound/firewire/bebob/bebob_midi.c | 162 ++++++++++++++++++++++++++++++++++++++ 5 files changed, 175 insertions(+), 1 deletion(-) create mode 100644 sound/firewire/bebob/bebob_midi.c diff --git a/sound/firewire/Kconfig b/sound/firewire/Kconfig index 9027b23..8081ca1 100644 --- a/sound/firewire/Kconfig +++ b/sound/firewire/Kconfig @@ -82,6 +82,7 @@ config SND_FIREWORKS config SND_BEBOB tristate "BridgeCo DM1000/1500 with BeBoB firmware" select SND_FIREWIRE_LIB + select SND_RAWMIDI help Say Y here to include support for FireWire devices based on BridgeCo DM1000/1500 with BeBoB firmware: diff --git a/sound/firewire/bebob/Makefile b/sound/firewire/bebob/Makefile index 757c40e..1e39e59 100644 --- a/sound/firewire/bebob/Makefile +++ b/sound/firewire/bebob/Makefile @@ -1,2 +1,3 @@ -snd-bebob-objs := bebob_command.o bebob_stream.o bebob_proc.o bebob.o +snd-bebob-objs := bebob_command.o bebob_stream.o bebob_proc.o bebob_midi.o \ + bebob.o obj-m += snd-bebob.o diff --git a/sound/firewire/bebob/bebob.c b/sound/firewire/bebob/bebob.c index b19d7c5..adb5493 100644 --- a/sound/firewire/bebob/bebob.c +++ b/sound/firewire/bebob/bebob.c @@ -169,6 +169,13 @@ bebob_probe(struct fw_unit *unit, snd_bebob_proc_init(bebob); + if ((bebob->midi_input_ports > 0) || + (bebob->midi_output_ports > 0)) { + err = snd_bebob_create_midi_devices(bebob); + if (err < 0) + goto error; + } + snd_card_set_dev(card, &unit->device); err = snd_card_register(card); if (err < 0) { diff --git a/sound/firewire/bebob/bebob.h b/sound/firewire/bebob/bebob.h index e513fd3..c8e774b 100644 --- a/sound/firewire/bebob/bebob.h +++ b/sound/firewire/bebob/bebob.h @@ -21,6 +21,7 @@ #include <sound/core.h> #include <sound/initval.h> #include <sound/info.h> +#include <sound/rawmidi.h> #include "../lib.h" #include "../fcp.h" @@ -178,6 +179,8 @@ void snd_bebob_stream_destroy_duplex(struct snd_bebob *bebob); void snd_bebob_proc_init(struct snd_bebob *bebob); +int snd_bebob_create_midi_devices(struct snd_bebob *bebob); + #define SND_BEBOB_DEV_ENTRY(vendor, model) \ { \ .match_flags = IEEE1394_MATCH_VENDOR_ID | \ diff --git a/sound/firewire/bebob/bebob_midi.c b/sound/firewire/bebob/bebob_midi.c new file mode 100644 index 0000000..10945fe16 --- /dev/null +++ b/sound/firewire/bebob/bebob_midi.c @@ -0,0 +1,162 @@ +/* + * bebob_midi.c - a part of driver for BeBoB based devices + * + * Copyright (c) 2013 Takashi Sakamoto + * + * Licensed under the terms of the GNU General Public License, version 2. + */ + +#include "bebob.h" + +static int midi_capture_open(struct snd_rawmidi_substream *substream) +{ + struct snd_bebob *bebob = substream->rmidi->private_data; + int err = 0; + + bebob->tx_midi_substreams++; + if (!amdtp_stream_running(&bebob->tx_stream)) + err = snd_bebob_stream_start_duplex(bebob, + &bebob->tx_stream, 0); + + return err; +} + +static int midi_playback_open(struct snd_rawmidi_substream *substream) +{ + struct snd_bebob *bebob = substream->rmidi->private_data; + int err = 0; + + bebob->rx_midi_substreams++; + if (!amdtp_stream_running(&bebob->rx_stream)) + err = snd_bebob_stream_start_duplex(bebob, + &bebob->rx_stream, 0); + + return err; +} + +static int midi_capture_close(struct snd_rawmidi_substream *substream) +{ + struct snd_bebob *bebob = substream->rmidi->private_data; + + if (--bebob->tx_midi_substreams == 0) + snd_bebob_stream_stop_duplex(bebob); + + return 0; +} + +static int midi_playback_close(struct snd_rawmidi_substream *substream) +{ + struct snd_bebob *bebob = substream->rmidi->private_data; + + if (--bebob->rx_midi_substreams == 0) + snd_bebob_stream_stop_duplex(bebob); + + return 0; +} + +static void midi_capture_trigger(struct snd_rawmidi_substream *substrm, int up) +{ + struct snd_bebob *bebob = substrm->rmidi->private_data; + unsigned long flags; + + spin_lock_irqsave(&bebob->lock, flags); + + if (up) + amdtp_stream_midi_trigger(&bebob->tx_stream, + substrm->number, substrm); + else + amdtp_stream_midi_trigger(&bebob->tx_stream, + substrm->number, NULL); + + spin_unlock_irqrestore(&bebob->lock, flags); + + return; +} + +static void midi_playback_trigger(struct snd_rawmidi_substream *substrm, int up) +{ + struct snd_bebob *bebob = substrm->rmidi->private_data; + unsigned long flags; + + spin_lock_irqsave(&bebob->lock, flags); + + if (up) + amdtp_stream_midi_trigger(&bebob->rx_stream, + substrm->number, substrm); + else + amdtp_stream_midi_trigger(&bebob->rx_stream, + substrm->number, NULL); + + spin_unlock_irqrestore(&bebob->lock, flags); + + return; +} + +static struct snd_rawmidi_ops midi_capture_ops = { + .open = midi_capture_open, + .close = midi_capture_close, + .trigger = midi_capture_trigger, +}; + +static struct snd_rawmidi_ops midi_playback_ops = { + .open = midi_playback_open, + .close = midi_playback_close, + .trigger = midi_playback_trigger, +}; + +static void set_midi_substream_names(struct snd_bebob *bebob, + struct snd_rawmidi_str *str) +{ + struct snd_rawmidi_substream *subs; + + list_for_each_entry(subs, &str->substreams, list) { + snprintf(subs->name, sizeof(subs->name), + "%s MIDI %d", + bebob->card->shortname, subs->number + 1); + } +} + +int snd_bebob_create_midi_devices(struct snd_bebob *bebob) +{ + struct snd_rawmidi *rmidi; + struct snd_rawmidi_str *str; + int err; + + /* create midi ports */ + err = snd_rawmidi_new(bebob->card, bebob->card->driver, 0, + bebob->midi_output_ports, bebob->midi_input_ports, + &rmidi); + if (err < 0) + return err; + + snprintf(rmidi->name, sizeof(rmidi->name), + "%s MIDI", bebob->card->shortname); + rmidi->private_data = bebob; + + if (bebob->midi_input_ports > 0) { + rmidi->info_flags |= SNDRV_RAWMIDI_INFO_INPUT; + + snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_INPUT, + &midi_capture_ops); + + str = &rmidi->streams[SNDRV_RAWMIDI_STREAM_INPUT]; + + set_midi_substream_names(bebob, str); + } + + if (bebob->midi_output_ports > 0) { + rmidi->info_flags |= SNDRV_RAWMIDI_INFO_OUTPUT; + + snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_OUTPUT, + &midi_playback_ops); + + str = &rmidi->streams[SNDRV_RAWMIDI_STREAM_OUTPUT]; + + set_midi_substream_names(bebob, str); + } + + if ((bebob->midi_output_ports > 0) && (bebob->midi_input_ports > 0)) + rmidi->info_flags |= SNDRV_RAWMIDI_INFO_DUPLEX; + + return 0; +} -- 1.8.3.2 |
From: Takashi S. <o-t...@sa...> - 2014-01-29 13:46:05
|
This commit allows this driver to support all of models which Terratec produced with DM1000/BeBoB. They are: - PHASE 24 FW - PHASE X24 FW - PHASE 88 Rack FW - EWS MIC2 - EWS MIC4 - Aureon 7.1 Firewire For Phase series, this commit adds a Terratec specific operation. To get source of clock. AV/C Audio Subunit command is used. For EWS series and Aureon, this module uses normal operations. Signed-off-by: Takashi Sakamoto <o-t...@sa...> --- sound/firewire/Kconfig | 3 ++ sound/firewire/bebob/Makefile | 2 +- sound/firewire/bebob/bebob.c | 11 ++++++ sound/firewire/bebob/bebob.h | 4 +++ sound/firewire/bebob/bebob_terratec.c | 68 +++++++++++++++++++++++++++++++++++ 5 files changed, 87 insertions(+), 1 deletion(-) create mode 100644 sound/firewire/bebob/bebob_terratec.c diff --git a/sound/firewire/Kconfig b/sound/firewire/Kconfig index 060fe7e..0a7918a 100644 --- a/sound/firewire/Kconfig +++ b/sound/firewire/Kconfig @@ -108,6 +108,9 @@ config SND_BEBOB * Lynx Aurora 8/16 (LT-FW) * ICON FireXon * PrismSound Orpheus/ADA-8XR + * TerraTec PHASE 24 FW/PHASE X24 FW/PHASE 88 Rack FW + * Terratec EWS MIC2/EWS MIC4 + * Terratec Aureon 7.1 Firewire To compile this driver as a module, choose M here: the module will be called snd-bebob. diff --git a/sound/firewire/bebob/Makefile b/sound/firewire/bebob/Makefile index e4b08e3..cb38dd1 100644 --- a/sound/firewire/bebob/Makefile +++ b/sound/firewire/bebob/Makefile @@ -1,3 +1,3 @@ snd-bebob-objs := bebob_command.o bebob_stream.o bebob_proc.o bebob_midi.o \ - bebob_pcm.o bebob_hwdep.o bebob.o + bebob_pcm.o bebob_hwdep.o bebob_terratec.o bebob.o obj-m += snd-bebob.o diff --git a/sound/firewire/bebob/bebob.c b/sound/firewire/bebob/bebob.c index a53eb13..42e6d6c 100644 --- a/sound/firewire/bebob/bebob.c +++ b/sound/firewire/bebob/bebob.c @@ -52,6 +52,7 @@ static unsigned int devices_used; #define VEN_LYNX 0x000019e5 #define VEN_ICON 0x00001a9e #define VEN_PRISMSOUND 0x00001198 +#define VEN_TERRATEC 0x00000aac static int name_device(struct snd_bebob *bebob, unsigned int vendor_id) @@ -294,6 +295,16 @@ static const struct ieee1394_device_id bebob_id_table[] = { SND_BEBOB_DEV_ENTRY(VEN_PRISMSOUND, 0x00010048, &spec_normal), /* PrismSound, ADA-8XR */ SND_BEBOB_DEV_ENTRY(VEN_PRISMSOUND, 0x0000ada8, &spec_normal), + /* TerraTec Electronic GmbH, PHASE 88 Rack FW */ + SND_BEBOB_DEV_ENTRY(VEN_TERRATEC, 0x00000003, &phase88_rack_spec), + /* TerraTec Electronic GmbH, PHASE 24 FW */ + SND_BEBOB_DEV_ENTRY(VEN_TERRATEC, 0x00000004, &phase24_series_spec), + /* TerraTec Electronic GmbH, Phase X24 FW */ + SND_BEBOB_DEV_ENTRY(VEN_TERRATEC, 0x00000007, &phase24_series_spec), + /* TerraTec Electronic GmbH, EWS MIC2/MIC8 */ + SND_BEBOB_DEV_ENTRY(VEN_TERRATEC, 0x00000005, &spec_normal), + /* Terratec Electronic GmbH, Aureon 7.1 Firewire */ + SND_BEBOB_DEV_ENTRY(VEN_TERRATEC, 0x00000002, &spec_normal), /* IDs are unknown but able to be supported */ /* Apogee, Mini-ME Firewire */ /* Apogee, Mini-DAC Firewire */ diff --git a/sound/firewire/bebob/bebob.h b/sound/firewire/bebob/bebob.h index ea53d56..3723f2b 100644 --- a/sound/firewire/bebob/bebob.h +++ b/sound/firewire/bebob/bebob.h @@ -228,6 +228,10 @@ int snd_bebob_create_pcm_devices(struct snd_bebob *bebob); int snd_bebob_create_hwdep_device(struct snd_bebob *bebob); +/* model specific operations */ +extern struct snd_bebob_spec phase88_rack_spec; +extern struct snd_bebob_spec phase24_series_spec; + #define SND_BEBOB_DEV_ENTRY(vendor, model, data) \ { \ .match_flags = IEEE1394_MATCH_VENDOR_ID | \ diff --git a/sound/firewire/bebob/bebob_terratec.c b/sound/firewire/bebob/bebob_terratec.c new file mode 100644 index 0000000..9c2cebf --- /dev/null +++ b/sound/firewire/bebob/bebob_terratec.c @@ -0,0 +1,68 @@ +/* + * bebob_terratec.c - a part of driver for BeBoB based devices + * + * Copyright (c) 2013 Takashi Sakamoto + * + * Licensed under the terms of the GNU General Public License, version 2. + */ + +#include "./bebob.h" + +static char *phase88_rack_clk_src_labels[] = { + SND_BEBOB_CLOCK_INTERNAL, "Digital In", "Word Clock" +}; +static int +phase88_rack_clk_src_get(struct snd_bebob *bebob, unsigned int *id) +{ + unsigned int enable_ext, enable_word; + int err; + + err = avc_audio_get_selector(bebob->unit, 0, 0, &enable_ext); + if (err < 0) + goto end; + err = avc_audio_get_selector(bebob->unit, 0, 0, &enable_word); + if (err < 0) + goto end; + + *id = (enable_ext & 0x01) || ((enable_word & 0x01) << 1); +end: + return err; +} + +static char *phase24_series_clk_src_labels[] = { + SND_BEBOB_CLOCK_INTERNAL, "Digital In" +}; +static int +phase24_series_clk_src_get(struct snd_bebob *bebob, unsigned int *id) +{ + return avc_audio_get_selector(bebob->unit, 0, 4, id); +} + +struct snd_bebob_rate_spec phase_series_rate_spec = { + .get = &snd_bebob_stream_get_rate, + .set = &snd_bebob_stream_set_rate, +}; + +/* PHASE 88 Rack FW */ +struct snd_bebob_clock_spec phase88_rack_clk = { + .num = ARRAY_SIZE(phase88_rack_clk_src_labels), + .labels = phase88_rack_clk_src_labels, + .get = &phase88_rack_clk_src_get, +}; +struct snd_bebob_spec phase88_rack_spec = { + .clock = &phase88_rack_clk, + .rate = &phase_series_rate_spec, + .meter = NULL +}; + +/* 'PHASE 24 FW' and 'PHASE X24 FW' */ +struct snd_bebob_clock_spec phase24_series_clk = { + .num = ARRAY_SIZE(phase24_series_clk_src_labels), + .labels = phase24_series_clk_src_labels, + .get = &phase24_series_clk_src_get, +}; +struct snd_bebob_spec phase24_series_spec = { + .clock = &phase24_series_clk, + .rate = &phase_series_rate_spec, + .meter = NULL +}; -- 1.8.3.2 |
From: Takashi S. <o-t...@sa...> - 2014-01-29 13:46:06
|
This commit allows this driver to support all of models which Yamaha produced with DM1000/BeBoB. They are: - GO44 - GO46 This commit adds Yamaha specific operations. To get source of clock, AV/C Audio Subunit command is used. I note that their appearances are similar to some models of TerraTec; 'Go44' is similar to 'PHASE 24 FW' and 'GO46' is similar to 'PHASE X24 FW'. But their combination of Audio/Music subunit are a bit different. Signed-off-by: Takashi Sakamoto <o-t...@sa...> --- sound/firewire/Kconfig | 1 + sound/firewire/bebob/Makefile | 3 ++- sound/firewire/bebob/bebob.c | 5 ++++ sound/firewire/bebob/bebob.h | 1 + sound/firewire/bebob/bebob_yamaha.c | 50 +++++++++++++++++++++++++++++++++++++ 5 files changed, 59 insertions(+), 1 deletion(-) create mode 100644 sound/firewire/bebob/bebob_yamaha.c diff --git a/sound/firewire/Kconfig b/sound/firewire/Kconfig index 0a7918a..e7605ff 100644 --- a/sound/firewire/Kconfig +++ b/sound/firewire/Kconfig @@ -111,6 +111,7 @@ config SND_BEBOB * TerraTec PHASE 24 FW/PHASE X24 FW/PHASE 88 Rack FW * Terratec EWS MIC2/EWS MIC4 * Terratec Aureon 7.1 Firewire + * Yamaha GO44/GO46 To compile this driver as a module, choose M here: the module will be called snd-bebob. diff --git a/sound/firewire/bebob/Makefile b/sound/firewire/bebob/Makefile index cb38dd1..c35eee1 100644 --- a/sound/firewire/bebob/Makefile +++ b/sound/firewire/bebob/Makefile @@ -1,3 +1,4 @@ snd-bebob-objs := bebob_command.o bebob_stream.o bebob_proc.o bebob_midi.o \ - bebob_pcm.o bebob_hwdep.o bebob_terratec.o bebob.o + bebob_pcm.o bebob_hwdep.o bebob_terratec.o bebob_yamaha.o \ + bebob.o obj-m += snd-bebob.o diff --git a/sound/firewire/bebob/bebob.c b/sound/firewire/bebob/bebob.c index 42e6d6c..19fe22a 100644 --- a/sound/firewire/bebob/bebob.c +++ b/sound/firewire/bebob/bebob.c @@ -53,6 +53,7 @@ static unsigned int devices_used; #define VEN_ICON 0x00001a9e #define VEN_PRISMSOUND 0x00001198 #define VEN_TERRATEC 0x00000aac +#define VEN_YAMAHA 0x0000a0de static int name_device(struct snd_bebob *bebob, unsigned int vendor_id) @@ -305,6 +306,10 @@ static const struct ieee1394_device_id bebob_id_table[] = { SND_BEBOB_DEV_ENTRY(VEN_TERRATEC, 0x00000005, &spec_normal), /* Terratec Electronic GmbH, Aureon 7.1 Firewire */ SND_BEBOB_DEV_ENTRY(VEN_TERRATEC, 0x00000002, &spec_normal), + /* Yamaha, GO44 */ + SND_BEBOB_DEV_ENTRY(VEN_YAMAHA, 0x0010000b, &yamaha_go_spec), + /* YAMAHA, GO46 */ + SND_BEBOB_DEV_ENTRY(VEN_YAMAHA, 0x0010000c, &yamaha_go_spec), /* IDs are unknown but able to be supported */ /* Apogee, Mini-ME Firewire */ /* Apogee, Mini-DAC Firewire */ diff --git a/sound/firewire/bebob/bebob.h b/sound/firewire/bebob/bebob.h index 3723f2b..1d1deda 100644 --- a/sound/firewire/bebob/bebob.h +++ b/sound/firewire/bebob/bebob.h @@ -231,6 +231,7 @@ int snd_bebob_create_hwdep_device(struct snd_bebob *bebob); /* model specific operations */ extern struct snd_bebob_spec phase88_rack_spec; extern struct snd_bebob_spec phase24_series_spec; +extern struct snd_bebob_spec yamaha_go_spec; #define SND_BEBOB_DEV_ENTRY(vendor, model, data) \ { \ diff --git a/sound/firewire/bebob/bebob_yamaha.c b/sound/firewire/bebob/bebob_yamaha.c new file mode 100644 index 0000000..f71af6b --- /dev/null +++ b/sound/firewire/bebob/bebob_yamaha.c @@ -0,0 +1,50 @@ +/* + * bebob_yamaha.c - a part of driver for BeBoB based devices + * + * Copyright (c) 2013 Takashi Sakamoto + * + * Licensed under the terms of the GNU General Public License, version 2. + */ + +#include "./bebob.h" + +/* + * NOTE: + * Yamaha GO44 is not designed to be used as stand-alone mixer. So any streams + * must be accompanied. If changing the state, a LED on the device starts to + * blink and its sync status is false. In this state, the device sounds nothing + * even if streaming. To start streaming at the current sampling rate is only + * way to revocer this state. GO46 is better for stand-alone mixer. + * + * Both of them have a capability to change its sampling rate up to 192.0kHz. + * At 192.0kHz, the device reports 4 PCM-in, 1 MIDI-in, 6 PCM-out, 1 MIDI-out. + * But Yamaha's driver reduce 2 PCM-in, 1 MIDI-in, 2 PCM-out, 1 MIDI-out to use + * 'Extended Stream Format Information Command - Single Request' in 'Additional + * AVC commands' defined by BridgeCo. + * This ALSA driver don't do this because a bit tiresome. Then isochronous + * streaming with many asynchronous transactions brings sounds with noises. + * Unfortunately current 'ffado-mixer' generated many asynchronous transaction + * to observe device's state, mainly check cmp connection and signal format. I + * reccomend users to close ffado-mixer at 192.0kHz if mixer is needless. + */ + +static char *clk_src_labels[] = {SND_BEBOB_CLOCK_INTERNAL, "SPDIF"}; +static int +clk_src_get(struct snd_bebob *bebob, unsigned int *id) +{ + return avc_audio_get_selector(bebob->unit, 0, 4, id); +} +static struct snd_bebob_clock_spec clock_spec = { + .num = ARRAY_SIZE(clk_src_labels), + .labels = clk_src_labels, + .get = &clk_src_get, +}; +static struct snd_bebob_rate_spec rate_spec = { + .get = &snd_bebob_stream_get_rate, + .set = &snd_bebob_stream_set_rate, +}; +struct snd_bebob_spec yamaha_go_spec = { + .clock = &clock_spec, + .rate = &rate_spec, + .meter = NULL +}; -- 1.8.3.2 |
From: Takashi S. <o-t...@sa...> - 2014-01-29 13:46:08
|
This commit adds proc interface to output infomation for debugging. - firmware information - sampling rate and clock source - physical metering (linear value) Signed-off-by: Takashi Sakamoto <o-t...@sa...> --- sound/firewire/fireworks/Makefile | 2 +- sound/firewire/fireworks/fireworks.c | 12 ++ sound/firewire/fireworks/fireworks.h | 11 ++ sound/firewire/fireworks/fireworks_proc.c | 187 ++++++++++++++++++++++++++++++ 4 files changed, 211 insertions(+), 1 deletion(-) create mode 100644 sound/firewire/fireworks/fireworks_proc.c diff --git a/sound/firewire/fireworks/Makefile b/sound/firewire/fireworks/Makefile index 1bccb65..52bd15e 100644 --- a/sound/firewire/fireworks/Makefile +++ b/sound/firewire/fireworks/Makefile @@ -1,3 +1,3 @@ snd-fireworks-objs := fireworks_transaction.o fireworks_command.o \ - fireworks_stream.o fireworks.o + fireworks_stream.o fireworks_proc.o fireworks.o obj-m += snd-fireworks.o diff --git a/sound/firewire/fireworks/fireworks.c b/sound/firewire/fireworks/fireworks.c index d0b3fff..cec865c 100644 --- a/sound/firewire/fireworks/fireworks.c +++ b/sound/firewire/fireworks/fireworks.c @@ -130,6 +130,16 @@ get_hardware_info(struct snd_efw *efw) efw->pcm_playback_channels[0] = hwinfo->amdtp_rx_pcm_channels; efw->pcm_playback_channels[1] = hwinfo->amdtp_rx_pcm_channels_2x; efw->pcm_playback_channels[2] = hwinfo->amdtp_rx_pcm_channels_4x; + + /* hardware metering */ + efw->phys_out = hwinfo->phys_out; + efw->phys_in = hwinfo->phys_in; + efw->phys_out_grp_count = hwinfo->phys_out_grp_count; + efw->phys_in_grp_count = hwinfo->phys_in_grp_count; + memcpy(&efw->phys_out_grps, hwinfo->phys_out_grps, + sizeof(struct snd_efw_phys_grp) * HWINFO_MAX_CAPS_GROUPS); + memcpy(&efw->phys_in_grps, hwinfo->phys_in_grps, + sizeof(struct snd_efw_phys_grp) * HWINFO_MAX_CAPS_GROUPS); end: kfree(hwinfo); return err; @@ -191,6 +201,8 @@ efw_probe(struct fw_unit *unit, if (err < 0) goto error; + snd_efw_proc_init(efw); + snd_card_set_dev(card, &unit->device); err = snd_card_register(card); if (err < 0) diff --git a/sound/firewire/fireworks/fireworks.h b/sound/firewire/fireworks/fireworks.h index 721e8dd..5891b79 100644 --- a/sound/firewire/fireworks/fireworks.h +++ b/sound/firewire/fireworks/fireworks.h @@ -21,6 +21,7 @@ #include <sound/core.h> #include <sound/initval.h> #include <sound/pcm.h> +#include <sound/info.h> #include "../packets-buffer.h" #include "../iso-resources.h" @@ -65,6 +66,14 @@ struct snd_efw { struct cmp_connection in_conn; unsigned int rx_midi_substreams; unsigned int tx_midi_substreams; + + /* hardware metering parameters */ + unsigned int phys_out; + unsigned int phys_in; + unsigned int phys_out_grp_count; + unsigned int phys_in_grp_count; + struct snd_efw_phys_grp phys_out_grps[HWINFO_MAX_CAPS_GROUPS]; + struct snd_efw_phys_grp phys_in_grps[HWINFO_MAX_CAPS_GROUPS]; }; struct snd_efw_transaction { @@ -178,6 +187,8 @@ int snd_efw_stream_stop_duplex(struct snd_efw *efw); void snd_efw_stream_update_duplex(struct snd_efw *efw); void snd_efw_stream_destroy_duplex(struct snd_efw *efw); +void snd_efw_proc_init(struct snd_efw *efw); + #define SND_EFW_DEV_ENTRY(vendor, model) \ { \ .match_flags = IEEE1394_MATCH_VENDOR_ID | \ diff --git a/sound/firewire/fireworks/fireworks_proc.c b/sound/firewire/fireworks/fireworks_proc.c new file mode 100644 index 0000000..389a68b --- /dev/null +++ b/sound/firewire/fireworks/fireworks_proc.c @@ -0,0 +1,187 @@ +/* + * fireworks_proc.c - a part of driver for Fireworks based devices + * + * Copyright (c) 2009-2010 Clemens Ladisch + * Copyright (c) 2013 Takashi Sakamoto + * + * Licensed under the terms of the GNU General Public License, version 2. + */ + +#include "./fireworks.h" + +static inline const char* +get_phys_name(struct snd_efw_phys_grp *grp) +{ + const char *ch_type[] = { + "Analog", "S/PDIF", "ADAT", "S/PDIF or ADAT", + "Mirroring", "Headphones", "I2S", "Guitar", + "Pirzo Guitar", "Guitar String", "Virtual", "Dummy" + }; + + if (grp->type < 10) + return ch_type[grp->type]; + else if (grp->type == 0x10000) + return ch_type[10]; + else + return ch_type[11]; +} + +static void +proc_read_hwinfo(struct snd_info_entry *entry, struct snd_info_buffer *buffer) +{ + struct snd_efw *efw = entry->private_data; + unsigned short i; + struct snd_efw_hwinfo hwinfo; + + if (snd_efw_command_get_hwinfo(efw, &hwinfo) < 0) + goto end; + + snd_iprintf(buffer, "guid_hi: 0x%X\n", hwinfo.guid_hi); + snd_iprintf(buffer, "guid_lo: 0x%X\n", hwinfo.guid_lo); + snd_iprintf(buffer, "type: 0x%X\n", hwinfo.type); + snd_iprintf(buffer, "version: 0x%X\n", hwinfo.version); + snd_iprintf(buffer, "vendor_name: %s\n", hwinfo.vendor_name); + snd_iprintf(buffer, "model_name: %s\n", hwinfo.model_name); + + snd_iprintf(buffer, "dsp_version: 0x%X\n", hwinfo.dsp_version); + snd_iprintf(buffer, "arm_version: 0x%X\n", hwinfo.arm_version); + snd_iprintf(buffer, "fpga_version: 0x%X\n", hwinfo.fpga_version); + + snd_iprintf(buffer, "flags: 0x%X\n", hwinfo.flags); + + snd_iprintf(buffer, "max_sample_rate: 0x%X\n", hwinfo.max_sample_rate); + snd_iprintf(buffer, "min_sample_rate: 0x%X\n", hwinfo.min_sample_rate); + snd_iprintf(buffer, "supported_clock: 0x%X\n", + hwinfo.supported_clocks); + + snd_iprintf(buffer, "phys out: 0x%X\n", hwinfo.phys_out); + snd_iprintf(buffer, "phys in: 0x%X\n", hwinfo.phys_in); + + snd_iprintf(buffer, "phys in grps: 0x%X\n", hwinfo.phys_in_grp_count); + for (i = 0; i < hwinfo.phys_in_grp_count; i++) { + snd_iprintf(buffer, + "phys in grp[0x%d]: type 0x%d, count 0x%d\n", + i, hwinfo.phys_out_grps[i].type, + hwinfo.phys_out_grps[i].count); + } + + snd_iprintf(buffer, "phys out grps: 0x%X\n", hwinfo.phys_out_grp_count); + for (i = 0; i < hwinfo.phys_out_grp_count; i++) { + snd_iprintf(buffer, + "phys out grps[0x%d]: type 0x%d, count 0x%d\n", + i, hwinfo.phys_out_grps[i].type, + hwinfo.phys_out_grps[i].count); + } + + snd_iprintf(buffer, "amdtp rx pcm channels 1x: 0x%X\n", + hwinfo.amdtp_rx_pcm_channels); + snd_iprintf(buffer, "amdtp tx pcm channels 1x: 0x%X\n", + hwinfo.amdtp_tx_pcm_channels); + snd_iprintf(buffer, "amdtp rx pcm channels 2x: 0x%X\n", + hwinfo.amdtp_rx_pcm_channels_2x); + snd_iprintf(buffer, "amdtp tx pcm channels 2x: 0x%X\n", + hwinfo.amdtp_tx_pcm_channels_2x); + snd_iprintf(buffer, "amdtp rx pcm channels 4x: 0x%X\n", + hwinfo.amdtp_rx_pcm_channels_4x); + snd_iprintf(buffer, "amdtp tx pcm channels 4x: 0x%X\n", + hwinfo.amdtp_tx_pcm_channels_4x); + + snd_iprintf(buffer, "midi out ports: 0x%X\n", hwinfo.midi_out_ports); + snd_iprintf(buffer, "midi in ports: 0x%X\n", hwinfo.midi_in_ports); + + snd_iprintf(buffer, "mixer playback channels: 0x%X\n", + hwinfo.mixer_playback_channels); + snd_iprintf(buffer, "mixer capture channels: 0x%X\n", + hwinfo.mixer_capture_channels); +end: + return; +} + +static void +proc_read_clock(struct snd_info_entry *entry, struct snd_info_buffer *buffer) +{ + struct snd_efw *efw = entry->private_data; + enum snd_efw_clock_source clock_source; + unsigned int sampling_rate; + + if (snd_efw_command_get_clock_source(efw, &clock_source) < 0) + goto end; + + if (snd_efw_command_get_sampling_rate(efw, &sampling_rate) < 0) + goto end; + + snd_iprintf(buffer, "Clock Source: %d\n", clock_source); + snd_iprintf(buffer, "Sampling Rate: %d\n", sampling_rate); +end: + return; +} + +/* + * NOTE: + * dB = 20 * log10(linear / 0x01000000) + * -144.0 dB when linear is 0 + */ +static void +proc_read_phys_meters(struct snd_info_entry *entry, + struct snd_info_buffer *buffer) +{ + struct snd_efw *efw = entry->private_data; + struct snd_efw_phys_meters *meters; + unsigned int g, c, m, max, size; + const char *name; + u32 *linear; + int err; + + size = sizeof(struct snd_efw_phys_meters) + + (efw->phys_in + efw->phys_out) * sizeof(u32); + meters = kzalloc(size, GFP_KERNEL); + if (meters == NULL) + return; + + err = snd_efw_command_get_phys_meters(efw, meters, size); + if (err < 0) + goto end; + + snd_iprintf(buffer, "Physical Meters:\n"); + + m = 0; + max = min(efw->phys_out, meters->out_meters); + linear = meters->values; + snd_iprintf(buffer, " %d Outputs:\n", max); + for (g = 0; g < efw->phys_out_grp_count; g++) { + name = get_phys_name(&efw->phys_out_grps[g]); + for (c = 0; c < efw->phys_out_grps[g].count; c++) { + if (m < max) + snd_iprintf(buffer, "\t%s [%d]: %d\n", + name, c, linear[m++]); + } + } + + m = 0; + max = min(efw->phys_in, meters->in_meters); + linear = meters->values + meters->out_meters; + snd_iprintf(buffer, " %d Inputs:\n", max); + for (g = 0; g < efw->phys_in_grp_count; g++) { + name = get_phys_name(&efw->phys_in_grps[g]); + for (c = 0; c < efw->phys_in_grps[g].count; c++) + if (m < max) + snd_iprintf(buffer, "\t%s [%d]: %d\n", + name, c, linear[m++]); + } +end: + kfree(meters); + return; +} + +void snd_efw_proc_init(struct snd_efw *efw) +{ + struct snd_info_entry *entry; + + if (!snd_card_proc_new(efw->card, "#firmware", &entry)) + snd_info_set_text_ops(entry, efw, proc_read_hwinfo); + if (!snd_card_proc_new(efw->card, "#clock", &entry)) + snd_info_set_text_ops(entry, efw, proc_read_clock); + if (!snd_card_proc_new(efw->card, "#meters", &entry)) + snd_info_set_text_ops(entry, efw, proc_read_phys_meters); + return; +} -- 1.8.3.2 |