summaryrefslogtreecommitdiff
path: root/sound/firewire
diff options
context:
space:
mode:
authorAndré Fabian Silva Delgado <emulatorman@parabola.nu>2015-09-08 01:01:14 -0300
committerAndré Fabian Silva Delgado <emulatorman@parabola.nu>2015-09-08 01:01:14 -0300
commite5fd91f1ef340da553f7a79da9540c3db711c937 (patch)
treeb11842027dc6641da63f4bcc524f8678263304a3 /sound/firewire
parent2a9b0348e685a63d97486f6749622b61e9e3292f (diff)
Linux-libre 4.2-gnu
Diffstat (limited to 'sound/firewire')
-rw-r--r--sound/firewire/Kconfig2
-rw-r--r--sound/firewire/amdtp.c271
-rw-r--r--sound/firewire/amdtp.h4
-rw-r--r--sound/firewire/bebob/bebob.c17
-rw-r--r--sound/firewire/bebob/bebob.h20
-rw-r--r--sound/firewire/bebob/bebob_focusrite.c33
-rw-r--r--sound/firewire/bebob/bebob_maudio.c23
-rw-r--r--sound/firewire/bebob/bebob_midi.c8
-rw-r--r--sound/firewire/bebob/bebob_pcm.c14
-rw-r--r--sound/firewire/bebob/bebob_proc.c22
-rw-r--r--sound/firewire/bebob/bebob_stream.c150
-rw-r--r--sound/firewire/bebob/bebob_terratec.c30
-rw-r--r--sound/firewire/bebob/bebob_yamaha.c20
-rw-r--r--sound/firewire/oxfw/oxfw-stream.c10
14 files changed, 408 insertions, 216 deletions
diff --git a/sound/firewire/Kconfig b/sound/firewire/Kconfig
index ecec54778..8850b7de1 100644
--- a/sound/firewire/Kconfig
+++ b/sound/firewire/Kconfig
@@ -95,6 +95,7 @@ config SND_BEBOB
* Tascam IF-FW/DM
* Behringer XENIX UFX 1204/1604
* Behringer Digital Mixer X32 series (X-UF Card)
+ * Behringer FCA610/1616
* Apogee Rosetta 200/400 (X-FireWire card)
* Apogee DA/AD/DD-16X (X-FireWire card)
* Apogee Ensemble
@@ -114,6 +115,7 @@ config SND_BEBOB
* M-Audio FireWire410/AudioPhile/Solo
* M-Audio Ozonic/NRV10/ProfireLightBridge
* M-Audio FireWire 1814/ProjectMix IO
+ * Digidesign Mbox 2 Pro
To compile this driver as a module, choose M here: the module
will be called snd-bebob.
diff --git a/sound/firewire/amdtp.c b/sound/firewire/amdtp.c
index bf20593d3..2a153d260 100644
--- a/sound/firewire/amdtp.c
+++ b/sound/firewire/amdtp.c
@@ -40,24 +40,28 @@
#define TAG_CIP 1
/* common isochronous packet header parameters */
-#define CIP_EOH (1u << 31)
+#define CIP_EOH_SHIFT 31
+#define CIP_EOH (1u << CIP_EOH_SHIFT)
#define CIP_EOH_MASK 0x80000000
-#define CIP_FMT_AM (0x10 << 24)
+#define CIP_SID_SHIFT 24
+#define CIP_SID_MASK 0x3f000000
+#define CIP_DBS_MASK 0x00ff0000
+#define CIP_DBS_SHIFT 16
+#define CIP_DBC_MASK 0x000000ff
+#define CIP_FMT_SHIFT 24
#define CIP_FMT_MASK 0x3f000000
+#define CIP_FDF_MASK 0x00ff0000
+#define CIP_FDF_SHIFT 16
#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 CIP_FMT_AM (0x10 << CIP_FMT_SHIFT)
+#define AMDTP_FDF_AM824 (0 << (CIP_FDF_SHIFT + 3))
#define AMDTP_FDF_NO_DATA 0xff
-#define AMDTP_DBS_MASK 0x00ff0000
-#define AMDTP_DBS_SHIFT 16
-#define AMDTP_DBC_MASK 0x000000ff
/* TODO: make these configurable */
#define INTERRUPT_INTERVAL 16
@@ -251,19 +255,24 @@ EXPORT_SYMBOL(amdtp_stream_set_parameters);
*/
unsigned int amdtp_stream_get_max_payload(struct amdtp_stream *s)
{
- return 8 + s->syt_interval * s->data_block_quadlets * 4;
+ unsigned int multiplier = 1;
+
+ if (s->flags & CIP_JUMBO_PAYLOAD)
+ multiplier = 5;
+
+ return 8 + s->syt_interval * s->data_block_quadlets * 4 * multiplier;
}
EXPORT_SYMBOL(amdtp_stream_get_max_payload);
-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_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 write_pcm_s16(struct amdtp_stream *s,
+ struct snd_pcm_substream *pcm,
+ __be32 *buffer, unsigned int frames);
+static void write_pcm_s32(struct amdtp_stream *s,
+ struct snd_pcm_substream *pcm,
+ __be32 *buffer, unsigned int frames);
+static void read_pcm_s32(struct amdtp_stream *s,
+ struct snd_pcm_substream *pcm,
+ __be32 *buffer, unsigned int frames);
/**
* amdtp_stream_set_pcm_format - set the PCM format
@@ -286,16 +295,16 @@ void amdtp_stream_set_pcm_format(struct amdtp_stream *s,
/* fall through */
case SNDRV_PCM_FORMAT_S16:
if (s->direction == AMDTP_OUT_STREAM) {
- s->transfer_samples = amdtp_write_s16;
+ s->transfer_samples = write_pcm_s16;
break;
}
WARN_ON(1);
/* fall through */
case SNDRV_PCM_FORMAT_S32:
if (s->direction == AMDTP_OUT_STREAM)
- s->transfer_samples = amdtp_write_s32;
+ s->transfer_samples = write_pcm_s32;
else
- s->transfer_samples = amdtp_read_s32;
+ s->transfer_samples = read_pcm_s32;
break;
}
}
@@ -316,17 +325,25 @@ void amdtp_stream_pcm_prepare(struct amdtp_stream *s)
}
EXPORT_SYMBOL(amdtp_stream_pcm_prepare);
-static unsigned int calculate_data_blocks(struct amdtp_stream *s)
+static unsigned int calculate_data_blocks(struct amdtp_stream *s,
+ unsigned int syt)
{
unsigned int phase, data_blocks;
- 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;
+ /* Blocking mode. */
+ if (s->flags & CIP_BLOCKING) {
+ /* This module generate empty packet for 'no data'. */
+ if (syt == CIP_SYT_NO_INFO)
+ data_blocks = 0;
+ else
+ data_blocks = s->syt_interval;
+ /* Non-blocking mode. */
} else {
- phase = s->data_block_state;
+ if (!cip_sfc_is_base_44100(s->sfc)) {
+ /* Sample_rate / 8000 is an integer, and precomputed. */
+ data_blocks = s->data_block_state;
+ } else {
+ phase = s->data_block_state;
/*
* This calculates the number of data blocks per packet so that
@@ -336,16 +353,17 @@ static unsigned int calculate_data_blocks(struct amdtp_stream *s)
* as possible in the sequence (to prevent underruns of the
* device's buffer).
*/
- if (s->sfc == CIP_SFC_44100)
- /* 6 6 5 6 5 6 5 ... */
- data_blocks = 5 + ((phase & 1) ^
- (phase == 0 || phase >= 40));
- else
- /* 12 11 11 11 11 ... or 23 22 22 22 22 ... */
- data_blocks = 11 * (s->sfc >> 1) + (phase == 0);
- if (++phase >= (80 >> (s->sfc >> 1)))
- phase = 0;
- s->data_block_state = phase;
+ if (s->sfc == CIP_SFC_44100)
+ /* 6 6 5 6 5 6 5 ... */
+ data_blocks = 5 + ((phase & 1) ^
+ (phase == 0 || phase >= 40));
+ else
+ /* 12 11 11 11 11 ... or 23 22 22 22 22 ... */
+ data_blocks = 11 * (s->sfc >> 1) + (phase == 0);
+ if (++phase >= (80 >> (s->sfc >> 1)))
+ phase = 0;
+ s->data_block_state = phase;
+ }
}
return data_blocks;
@@ -394,9 +412,9 @@ static unsigned int calculate_syt(struct amdtp_stream *s,
}
}
-static void amdtp_write_s32(struct amdtp_stream *s,
- struct snd_pcm_substream *pcm,
- __be32 *buffer, unsigned int frames)
+static void write_pcm_s32(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;
@@ -419,9 +437,9 @@ static void amdtp_write_s32(struct amdtp_stream *s,
}
}
-static void amdtp_write_s16(struct amdtp_stream *s,
- struct snd_pcm_substream *pcm,
- __be32 *buffer, unsigned int frames)
+static void write_pcm_s16(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;
@@ -444,9 +462,9 @@ static void amdtp_write_s16(struct amdtp_stream *s,
}
}
-static void amdtp_read_s32(struct amdtp_stream *s,
- struct snd_pcm_substream *pcm,
- __be32 *buffer, unsigned int frames)
+static void read_pcm_s32(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;
@@ -468,8 +486,8 @@ static void amdtp_read_s32(struct amdtp_stream *s,
}
}
-static void amdtp_fill_pcm_silence(struct amdtp_stream *s,
- __be32 *buffer, unsigned int frames)
+static void write_pcm_silence(struct amdtp_stream *s,
+ __be32 *buffer, unsigned int frames)
{
unsigned int i, c;
@@ -510,8 +528,8 @@ static void midi_rate_use_one_byte(struct amdtp_stream *s, unsigned int port)
s->midi_fifo_used[port] += amdtp_rate_table[s->sfc];
}
-static void amdtp_fill_midi(struct amdtp_stream *s,
- __be32 *buffer, unsigned int frames)
+static void write_midi_messages(struct amdtp_stream *s,
+ __be32 *buffer, unsigned int frames)
{
unsigned int f, port;
u8 *b;
@@ -537,8 +555,8 @@ static void amdtp_fill_midi(struct amdtp_stream *s,
}
}
-static void amdtp_pull_midi(struct amdtp_stream *s,
- __be32 *buffer, unsigned int frames)
+static void read_midi_messages(struct amdtp_stream *s,
+ __be32 *buffer, unsigned int frames)
{
unsigned int f, port;
int len;
@@ -633,57 +651,48 @@ 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 syt)
+static int handle_out_packet(struct amdtp_stream *s, unsigned int data_blocks,
+ unsigned int syt)
{
__be32 *buffer;
- unsigned int data_blocks, payload_length;
+ unsigned int payload_length;
struct snd_pcm_substream *pcm;
- if (s->packet_index < 0)
- return;
-
- /* this module generate empty packet for 'no data' */
- if (!(s->flags & CIP_BLOCKING) || (syt != CIP_SYT_NO_INFO))
- data_blocks = calculate_data_blocks(s);
- else
- data_blocks = 0;
-
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_quadlets << CIP_DBS_SHIFT) |
s->data_block_counter);
buffer[1] = cpu_to_be32(CIP_EOH | CIP_FMT_AM | AMDTP_FDF_AM824 |
- (s->sfc << CIP_FDF_SFC_SHIFT) | syt);
+ (s->sfc << CIP_FDF_SHIFT) | syt);
buffer += 2;
pcm = ACCESS_ONCE(s->pcm);
if (pcm)
s->transfer_samples(s, pcm, buffer, data_blocks);
else
- amdtp_fill_pcm_silence(s, buffer, data_blocks);
+ write_pcm_silence(s, buffer, data_blocks);
if (s->midi_ports)
- amdtp_fill_midi(s, buffer, data_blocks);
+ write_midi_messages(s, buffer, data_blocks);
s->data_block_counter = (s->data_block_counter + data_blocks) & 0xff;
payload_length = 8 + data_blocks * 4 * s->data_block_quadlets;
- if (queue_out_packet(s, payload_length, false) < 0) {
- s->packet_index = -1;
- amdtp_stream_pcm_abort(s);
- return;
- }
+ if (queue_out_packet(s, payload_length, false) < 0)
+ return -EIO;
if (pcm)
update_pcm_pointers(s, pcm, data_blocks);
+
+ /* No need to return the number of handled data blocks. */
+ return 0;
}
-static void handle_in_packet(struct amdtp_stream *s,
- unsigned int payload_quadlets,
- __be32 *buffer)
+static int handle_in_packet(struct amdtp_stream *s,
+ unsigned int payload_quadlets, __be32 *buffer,
+ unsigned int *data_blocks)
{
u32 cip_header[2];
- unsigned int data_blocks, data_block_quadlets, data_block_counter,
- dbc_interval;
+ unsigned int data_block_quadlets, data_block_counter, dbc_interval;
struct snd_pcm_substream *pcm = NULL;
bool lost;
@@ -700,33 +709,34 @@ static void handle_in_packet(struct amdtp_stream *s,
dev_info_ratelimited(&s->unit->device,
"Invalid CIP header for AMDTP: %08X:%08X\n",
cip_header[0], cip_header[1]);
+ *data_blocks = 0;
goto end;
}
/* Calculate data blocks */
if (payload_quadlets < 3 ||
((cip_header[1] & CIP_FDF_MASK) ==
- (AMDTP_FDF_NO_DATA << CIP_FDF_SFC_SHIFT))) {
- data_blocks = 0;
+ (AMDTP_FDF_NO_DATA << CIP_FDF_SHIFT))) {
+ *data_blocks = 0;
} else {
data_block_quadlets =
- (cip_header[0] & AMDTP_DBS_MASK) >> AMDTP_DBS_SHIFT;
+ (cip_header[0] & CIP_DBS_MASK) >> CIP_DBS_SHIFT;
/* avoid division by zero */
if (data_block_quadlets == 0) {
- dev_info_ratelimited(&s->unit->device,
+ dev_err(&s->unit->device,
"Detect invalid value in dbs field: %08X\n",
cip_header[0]);
- goto err;
+ return -EPROTO;
}
if (s->flags & CIP_WRONG_DBS)
data_block_quadlets = s->data_block_quadlets;
- data_blocks = (payload_quadlets - 2) / data_block_quadlets;
+ *data_blocks = (payload_quadlets - 2) / data_block_quadlets;
}
/* Check data block counter continuity */
- data_block_counter = cip_header[0] & AMDTP_DBC_MASK;
- if (data_blocks == 0 && (s->flags & CIP_EMPTY_HAS_WRONG_DBC) &&
+ data_block_counter = cip_header[0] & CIP_DBC_MASK;
+ if (*data_blocks == 0 && (s->flags & CIP_EMPTY_HAS_WRONG_DBC) &&
s->data_block_counter != UINT_MAX)
data_block_counter = s->data_block_counter;
@@ -737,49 +747,46 @@ static void handle_in_packet(struct amdtp_stream *s,
} else if (!(s->flags & CIP_DBC_IS_END_EVENT)) {
lost = data_block_counter != s->data_block_counter;
} else {
- if ((data_blocks > 0) && (s->tx_dbc_interval > 0))
+ if ((*data_blocks > 0) && (s->tx_dbc_interval > 0))
dbc_interval = s->tx_dbc_interval;
else
- dbc_interval = data_blocks;
+ dbc_interval = *data_blocks;
lost = data_block_counter !=
((s->data_block_counter + dbc_interval) & 0xff);
}
if (lost) {
- dev_info(&s->unit->device,
- "Detect discontinuity of CIP: %02X %02X\n",
- s->data_block_counter, data_block_counter);
- goto err;
+ dev_err(&s->unit->device,
+ "Detect discontinuity of CIP: %02X %02X\n",
+ s->data_block_counter, data_block_counter);
+ return -EIO;
}
- if (data_blocks > 0) {
+ if (*data_blocks > 0) {
buffer += 2;
pcm = ACCESS_ONCE(s->pcm);
if (pcm)
- s->transfer_samples(s, pcm, buffer, data_blocks);
+ s->transfer_samples(s, pcm, buffer, *data_blocks);
if (s->midi_ports)
- amdtp_pull_midi(s, buffer, data_blocks);
+ read_midi_messages(s, buffer, *data_blocks);
}
if (s->flags & CIP_DBC_IS_END_EVENT)
s->data_block_counter = data_block_counter;
else
s->data_block_counter =
- (data_block_counter + data_blocks) & 0xff;
+ (data_block_counter + *data_blocks) & 0xff;
end:
if (queue_in_packet(s) < 0)
- goto err;
+ return -EIO;
if (pcm)
- update_pcm_pointers(s, pcm, data_blocks);
+ update_pcm_pointers(s, pcm, *data_blocks);
- return;
-err:
- s->packet_index = -1;
- amdtp_stream_pcm_abort(s);
+ return 0;
}
static void out_stream_callback(struct fw_iso_context *context, u32 cycle,
@@ -788,6 +795,10 @@ static void out_stream_callback(struct fw_iso_context *context, u32 cycle,
{
struct amdtp_stream *s = private_data;
unsigned int i, syt, packets = header_length / 4;
+ unsigned int data_blocks;
+
+ if (s->packet_index < 0)
+ return;
/*
* Compute the cycle of the last queued packet.
@@ -798,8 +809,15 @@ static void out_stream_callback(struct fw_iso_context *context, u32 cycle,
for (i = 0; i < packets; ++i) {
syt = calculate_syt(s, ++cycle);
- handle_out_packet(s, syt);
+ data_blocks = calculate_data_blocks(s, syt);
+
+ if (handle_out_packet(s, data_blocks, syt) < 0) {
+ s->packet_index = -1;
+ amdtp_stream_pcm_abort(s);
+ return;
+ }
}
+
fw_iso_context_queue_flush(s->context);
}
@@ -808,32 +826,55 @@ static void in_stream_callback(struct fw_iso_context *context, u32 cycle,
void *private_data)
{
struct amdtp_stream *s = private_data;
- unsigned int p, syt, packets, payload_quadlets;
+ unsigned int p, syt, packets;
+ unsigned int payload_quadlets, max_payload_quadlets;
+ unsigned int data_blocks;
__be32 *buffer, *headers = header;
+ if (s->packet_index < 0)
+ return;
+
/* The number of packets in buffer */
packets = header_length / IN_PACKET_HEADER_SIZE;
+ /* For buffer-over-run prevention. */
+ max_payload_quadlets = amdtp_stream_get_max_payload(s) / 4;
+
for (p = 0; p < packets; p++) {
- if (s->packet_index < 0)
+ buffer = s->buffer.packets[s->packet_index].buffer;
+
+ /* The number of quadlets in this packet */
+ payload_quadlets =
+ (be32_to_cpu(headers[p]) >> ISO_DATA_LENGTH_SHIFT) / 4;
+ if (payload_quadlets > max_payload_quadlets) {
+ dev_err(&s->unit->device,
+ "Detect jumbo payload: %02x %02x\n",
+ payload_quadlets, max_payload_quadlets);
+ s->packet_index = -1;
break;
+ }
- buffer = s->buffer.packets[s->packet_index].buffer;
+ if (handle_in_packet(s, payload_quadlets, buffer,
+ &data_blocks) < 0) {
+ s->packet_index = -1;
+ break;
+ }
/* Process sync slave stream */
if (s->sync_slave && s->sync_slave->callbacked) {
syt = be32_to_cpu(buffer[1]) & CIP_SYT_MASK;
- handle_out_packet(s->sync_slave, syt);
+ if (handle_out_packet(s->sync_slave,
+ data_blocks, syt) < 0) {
+ s->packet_index = -1;
+ break;
+ }
}
-
- /* The number of quadlets in this packet */
- payload_quadlets =
- (be32_to_cpu(headers[p]) >> ISO_DATA_LENGTH_SHIFT) / 4;
- handle_in_packet(s, payload_quadlets, buffer);
}
/* Queueing error or detecting discontinuity */
if (s->packet_index < 0) {
+ amdtp_stream_pcm_abort(s);
+
/* Abort sync slave. */
if (s->sync_slave) {
s->sync_slave->packet_index = -1;
@@ -873,7 +914,7 @@ static void amdtp_stream_first_callback(struct fw_iso_context *context,
if (s->direction == AMDTP_IN_STREAM)
context->callback.sc = in_stream_callback;
- else if ((s->flags & CIP_BLOCKING) && (s->flags & CIP_SYNC_TO_DEVICE))
+ else if (s->flags & CIP_SYNC_TO_DEVICE)
context->callback.sc = slave_stream_callback;
else
context->callback.sc = out_stream_callback;
@@ -1014,8 +1055,10 @@ EXPORT_SYMBOL(amdtp_stream_pcm_pointer);
*/
void amdtp_stream_update(struct amdtp_stream *s)
{
+ /* Precomputing. */
ACCESS_ONCE(s->source_node_id_field) =
- (fw_parent_device(s->unit)->card->node_id & 0x3f) << 24;
+ (fw_parent_device(s->unit)->card->node_id << CIP_SID_SHIFT) &
+ CIP_SID_MASK;
}
EXPORT_SYMBOL(amdtp_stream_update);
diff --git a/sound/firewire/amdtp.h b/sound/firewire/amdtp.h
index 25c905537..b2cf9e756 100644
--- a/sound/firewire/amdtp.h
+++ b/sound/firewire/amdtp.h
@@ -29,6 +29,9 @@
* packet is not continuous from an initial value.
* @CIP_EMPTY_HAS_WRONG_DBC: Only for in-stream. The value of dbc in empty
* packet is wrong but the others are correct.
+ * @CIP_JUMBO_PAYLOAD: Only for in-stream. The number of data blocks in an
+ * packet is larger than IEC 61883-6 defines. Current implementation
+ * allows 5 times as large as IEC 61883-6 defines.
*/
enum cip_flags {
CIP_NONBLOCKING = 0x00,
@@ -40,6 +43,7 @@ enum cip_flags {
CIP_SKIP_DBC_ZERO_CHECK = 0x20,
CIP_SKIP_INIT_DBC_CHECK = 0x40,
CIP_EMPTY_HAS_WRONG_DBC = 0x80,
+ CIP_JUMBO_PAYLOAD = 0x100,
};
/**
diff --git a/sound/firewire/bebob/bebob.c b/sound/firewire/bebob/bebob.c
index 611b7dae7..27a04ac8f 100644
--- a/sound/firewire/bebob/bebob.c
+++ b/sound/firewire/bebob/bebob.c
@@ -33,6 +33,7 @@ static DEFINE_MUTEX(devices_mutex);
static DECLARE_BITMAP(devices_used, SNDRV_CARDS);
/* Offsets from information register. */
+#define INFO_OFFSET_BEBOB_VERSION 0x08
#define INFO_OFFSET_GUID 0x10
#define INFO_OFFSET_HW_MODEL_ID 0x18
#define INFO_OFFSET_HW_MODEL_REVISION 0x1c
@@ -57,6 +58,7 @@ static DECLARE_BITMAP(devices_used, SNDRV_CARDS);
#define VEN_FOCUSRITE 0x0000130e
#define VEN_MAUDIO1 0x00000d6c
#define VEN_MAUDIO2 0x000007f5
+#define VEN_DIGIDESIGN 0x00a07e
#define MODEL_FOCUSRITE_SAFFIRE_BOTH 0x00000000
#define MODEL_MAUDIO_AUDIOPHILE_BOTH 0x00010060
@@ -72,6 +74,7 @@ name_device(struct snd_bebob *bebob, unsigned int vendor_id)
u32 hw_id;
u32 data[2] = {0};
u32 revision;
+ u32 version;
int err;
/* get vendor name from root directory */
@@ -104,6 +107,12 @@ name_device(struct snd_bebob *bebob, unsigned int vendor_id)
if (err < 0)
goto end;
+ err = snd_bebob_read_quad(bebob->unit, INFO_OFFSET_BEBOB_VERSION,
+ &version);
+ if (err < 0)
+ goto end;
+ bebob->version = version;
+
strcpy(bebob->card->driver, "BeBoB");
strcpy(bebob->card->shortname, model);
strcpy(bebob->card->mixername, model);
@@ -364,6 +373,10 @@ static const struct ieee1394_device_id bebob_id_table[] = {
SND_BEBOB_DEV_ENTRY(VEN_BEHRINGER, 0x00001604, &spec_normal),
/* Behringer, Digital Mixer X32 series (X-UF Card) */
SND_BEBOB_DEV_ENTRY(VEN_BEHRINGER, 0x00000006, &spec_normal),
+ /* Behringer, F-Control Audio 1616 */
+ SND_BEBOB_DEV_ENTRY(VEN_BEHRINGER, 0x001616, &spec_normal),
+ /* Behringer, F-Control Audio 610 */
+ SND_BEBOB_DEV_ENTRY(VEN_BEHRINGER, 0x000610, &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, &spec_normal),
@@ -433,11 +446,11 @@ static const struct ieee1394_device_id bebob_id_table[] = {
/* M-Audio ProjectMix */
SND_BEBOB_DEV_ENTRY(VEN_MAUDIO1, MODEL_MAUDIO_PROJECTMIX,
&maudio_special_spec),
+ /* Digidesign Mbox 2 Pro */
+ SND_BEBOB_DEV_ENTRY(VEN_DIGIDESIGN, 0x0000a9, &spec_normal),
/* 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 */
diff --git a/sound/firewire/bebob/bebob.h b/sound/firewire/bebob/bebob.h
index dfbcd2331..d23caca7f 100644
--- a/sound/firewire/bebob/bebob.h
+++ b/sound/firewire/bebob/bebob.h
@@ -49,10 +49,15 @@ struct snd_bebob_stream_formation {
extern const unsigned int snd_bebob_rate_table[SND_BEBOB_STRM_FMT_ENTRIES];
/* device specific operations */
-#define SND_BEBOB_CLOCK_INTERNAL "Internal"
+enum snd_bebob_clock_type {
+ SND_BEBOB_CLOCK_TYPE_INTERNAL = 0,
+ SND_BEBOB_CLOCK_TYPE_EXTERNAL,
+ SND_BEBOB_CLOCK_TYPE_SYT,
+};
struct snd_bebob_clock_spec {
unsigned int num;
const char *const *labels;
+ enum snd_bebob_clock_type *types;
int (*get)(struct snd_bebob *bebob, unsigned int *id);
};
struct snd_bebob_rate_spec {
@@ -92,8 +97,7 @@ struct snd_bebob {
struct amdtp_stream rx_stream;
struct cmp_connection out_conn;
struct cmp_connection in_conn;
- atomic_t capture_substreams;
- atomic_t playback_substreams;
+ atomic_t substreams_counter;
struct snd_bebob_stream_formation
tx_stream_formations[SND_BEBOB_STRM_FMT_ENTRIES];
@@ -110,6 +114,9 @@ struct snd_bebob {
/* for M-Audio special devices */
void *maudio_special_quirk;
bool deferred_registration;
+
+ /* For BeBoB version quirk. */
+ unsigned int version;
};
static inline int
@@ -159,7 +166,8 @@ enum avc_bridgeco_plug_type {
AVC_BRIDGECO_PLUG_TYPE_MIDI = 0x02,
AVC_BRIDGECO_PLUG_TYPE_SYNC = 0x03,
AVC_BRIDGECO_PLUG_TYPE_ANA = 0x04,
- AVC_BRIDGECO_PLUG_TYPE_DIG = 0x05
+ AVC_BRIDGECO_PLUG_TYPE_DIG = 0x05,
+ AVC_BRIDGECO_PLUG_TYPE_ADDITION = 0x06
};
static inline void
avc_bridgeco_fill_unit_addr(u8 buf[AVC_BRIDGECO_ADDR_BYTES],
@@ -205,8 +213,8 @@ int avc_bridgeco_get_plug_strm_fmt(struct fw_unit *unit,
/* for AMDTP streaming */
int snd_bebob_stream_get_rate(struct snd_bebob *bebob, unsigned int *rate);
int snd_bebob_stream_set_rate(struct snd_bebob *bebob, unsigned int rate);
-int snd_bebob_stream_check_internal_clock(struct snd_bebob *bebob,
- bool *internal);
+int snd_bebob_stream_get_clock_src(struct snd_bebob *bebob,
+ enum snd_bebob_clock_type *src);
int snd_bebob_stream_discover(struct snd_bebob *bebob);
int snd_bebob_stream_init_duplex(struct snd_bebob *bebob);
int snd_bebob_stream_start_duplex(struct snd_bebob *bebob, unsigned int rate);
diff --git a/sound/firewire/bebob/bebob_focusrite.c b/sound/firewire/bebob/bebob_focusrite.c
index fc67c1b7c..a1a39494e 100644
--- a/sound/firewire/bebob/bebob_focusrite.c
+++ b/sound/firewire/bebob/bebob_focusrite.c
@@ -103,11 +103,17 @@ saffire_write_quad(struct snd_bebob *bebob, u64 offset, u32 value)
&data, sizeof(__be32), 0);
}
-static const char *const saffirepro_10_clk_src_labels[] = {
- SND_BEBOB_CLOCK_INTERNAL, "S/PDIF", "Word Clock"
+static enum snd_bebob_clock_type saffirepro_10_clk_src_types[] = {
+ SND_BEBOB_CLOCK_TYPE_INTERNAL,
+ SND_BEBOB_CLOCK_TYPE_EXTERNAL, /* S/PDIF */
+ SND_BEBOB_CLOCK_TYPE_EXTERNAL, /* Word Clock */
};
-static const char *const saffirepro_26_clk_src_labels[] = {
- SND_BEBOB_CLOCK_INTERNAL, "S/PDIF", "ADAT1", "ADAT2", "Word Clock"
+static enum snd_bebob_clock_type saffirepro_26_clk_src_types[] = {
+ SND_BEBOB_CLOCK_TYPE_INTERNAL,
+ SND_BEBOB_CLOCK_TYPE_EXTERNAL, /* S/PDIF */
+ SND_BEBOB_CLOCK_TYPE_EXTERNAL, /* ADAT1 */
+ SND_BEBOB_CLOCK_TYPE_EXTERNAL, /* ADAT2 */
+ SND_BEBOB_CLOCK_TYPE_EXTERNAL, /* Word Clock */
};
/* Value maps between registers and labels for SaffirePro 10/26. */
static const signed char saffirepro_clk_maps[][SAFFIREPRO_CLOCK_SOURCE_COUNT] = {
@@ -178,7 +184,7 @@ saffirepro_both_clk_src_get(struct snd_bebob *bebob, unsigned int *id)
goto end;
/* depending on hardware, use a different mapping */
- if (bebob->spec->clock->labels == saffirepro_10_clk_src_labels)
+ if (bebob->spec->clock->types == saffirepro_10_clk_src_types)
map = saffirepro_clk_maps[0];
else
map = saffirepro_clk_maps[1];
@@ -195,8 +201,9 @@ end:
}
struct snd_bebob_spec saffire_le_spec;
-static const char *const saffire_both_clk_src_labels[] = {
- SND_BEBOB_CLOCK_INTERNAL, "S/PDIF"
+static enum snd_bebob_clock_type saffire_both_clk_src_types[] = {
+ SND_BEBOB_CLOCK_TYPE_INTERNAL,
+ SND_BEBOB_CLOCK_TYPE_EXTERNAL,
};
static int
saffire_both_clk_src_get(struct snd_bebob *bebob, unsigned int *id)
@@ -259,8 +266,8 @@ static struct snd_bebob_rate_spec saffirepro_both_rate_spec = {
};
/* Saffire Pro 26 I/O */
static struct snd_bebob_clock_spec saffirepro_26_clk_spec = {
- .num = ARRAY_SIZE(saffirepro_26_clk_src_labels),
- .labels = saffirepro_26_clk_src_labels,
+ .num = ARRAY_SIZE(saffirepro_26_clk_src_types),
+ .types = saffirepro_26_clk_src_types,
.get = &saffirepro_both_clk_src_get,
};
struct snd_bebob_spec saffirepro_26_spec = {
@@ -270,8 +277,8 @@ struct snd_bebob_spec saffirepro_26_spec = {
};
/* Saffire Pro 10 I/O */
static struct snd_bebob_clock_spec saffirepro_10_clk_spec = {
- .num = ARRAY_SIZE(saffirepro_10_clk_src_labels),
- .labels = saffirepro_10_clk_src_labels,
+ .num = ARRAY_SIZE(saffirepro_10_clk_src_types),
+ .types = saffirepro_10_clk_src_types,
.get = &saffirepro_both_clk_src_get,
};
struct snd_bebob_spec saffirepro_10_spec = {
@@ -285,8 +292,8 @@ static struct snd_bebob_rate_spec saffire_both_rate_spec = {
.set = &snd_bebob_stream_set_rate,
};
static struct snd_bebob_clock_spec saffire_both_clk_spec = {
- .num = ARRAY_SIZE(saffire_both_clk_src_labels),
- .labels = saffire_both_clk_src_labels,
+ .num = ARRAY_SIZE(saffire_both_clk_src_types),
+ .types = saffire_both_clk_src_types,
.get = &saffire_both_clk_src_get,
};
/* Saffire LE */
diff --git a/sound/firewire/bebob/bebob_maudio.c b/sound/firewire/bebob/bebob_maudio.c
index 9ee25a63f..057495d54 100644
--- a/sound/firewire/bebob/bebob_maudio.c
+++ b/sound/firewire/bebob/bebob_maudio.c
@@ -340,9 +340,12 @@ end:
}
/* Clock source control for special firmware */
-static const char *const special_clk_labels[] = {
- SND_BEBOB_CLOCK_INTERNAL " with Digital Mute", "Digital",
- "Word Clock", SND_BEBOB_CLOCK_INTERNAL};
+static enum snd_bebob_clock_type special_clk_types[] = {
+ SND_BEBOB_CLOCK_TYPE_INTERNAL, /* With digital mute */
+ SND_BEBOB_CLOCK_TYPE_EXTERNAL, /* SPDIF/ADAT */
+ SND_BEBOB_CLOCK_TYPE_EXTERNAL, /* Word Clock */
+ SND_BEBOB_CLOCK_TYPE_INTERNAL,
+};
static int special_clk_get(struct snd_bebob *bebob, unsigned int *id)
{
struct special_params *params = bebob->maudio_special_quirk;
@@ -352,7 +355,13 @@ static int special_clk_get(struct snd_bebob *bebob, unsigned int *id)
static int special_clk_ctl_info(struct snd_kcontrol *kctl,
struct snd_ctl_elem_info *einf)
{
- return snd_ctl_enum_info(einf, 1, ARRAY_SIZE(special_clk_labels),
+ static const char *const special_clk_labels[] = {
+ "Internal with Digital Mute",
+ "Digital",
+ "Word Clock",
+ "Internal"
+ };
+ return snd_ctl_enum_info(einf, 1, ARRAY_SIZE(special_clk_types),
special_clk_labels);
}
static int special_clk_ctl_get(struct snd_kcontrol *kctl,
@@ -371,7 +380,7 @@ static int special_clk_ctl_put(struct snd_kcontrol *kctl,
int err, id;
id = uval->value.enumerated.item[0];
- if (id >= ARRAY_SIZE(special_clk_labels))
+ if (id >= ARRAY_SIZE(special_clk_types))
return -EINVAL;
mutex_lock(&bebob->mutex);
@@ -708,8 +717,8 @@ static struct snd_bebob_rate_spec special_rate_spec = {
.set = &special_set_rate,
};
static struct snd_bebob_clock_spec special_clk_spec = {
- .num = ARRAY_SIZE(special_clk_labels),
- .labels = special_clk_labels,
+ .num = ARRAY_SIZE(special_clk_types),
+ .types = special_clk_types,
.get = &special_clk_get,
};
static struct snd_bebob_meter_spec special_meter_spec = {
diff --git a/sound/firewire/bebob/bebob_midi.c b/sound/firewire/bebob/bebob_midi.c
index 63343d578..568114392 100644
--- a/sound/firewire/bebob/bebob_midi.c
+++ b/sound/firewire/bebob/bebob_midi.c
@@ -17,7 +17,7 @@ static int midi_capture_open(struct snd_rawmidi_substream *substream)
if (err < 0)
goto end;
- atomic_inc(&bebob->capture_substreams);
+ atomic_inc(&bebob->substreams_counter);
err = snd_bebob_stream_start_duplex(bebob, 0);
if (err < 0)
snd_bebob_stream_lock_release(bebob);
@@ -34,7 +34,7 @@ static int midi_playback_open(struct snd_rawmidi_substream *substream)
if (err < 0)
goto end;
- atomic_inc(&bebob->playback_substreams);
+ atomic_inc(&bebob->substreams_counter);
err = snd_bebob_stream_start_duplex(bebob, 0);
if (err < 0)
snd_bebob_stream_lock_release(bebob);
@@ -46,7 +46,7 @@ static int midi_capture_close(struct snd_rawmidi_substream *substream)
{
struct snd_bebob *bebob = substream->rmidi->private_data;
- atomic_dec(&bebob->capture_substreams);
+ atomic_dec(&bebob->substreams_counter);
snd_bebob_stream_stop_duplex(bebob);
snd_bebob_stream_lock_release(bebob);
@@ -57,7 +57,7 @@ static int midi_playback_close(struct snd_rawmidi_substream *substream)
{
struct snd_bebob *bebob = substream->rmidi->private_data;
- atomic_dec(&bebob->playback_substreams);
+ atomic_dec(&bebob->substreams_counter);
snd_bebob_stream_stop_duplex(bebob);
snd_bebob_stream_lock_release(bebob);
diff --git a/sound/firewire/bebob/bebob_pcm.c b/sound/firewire/bebob/bebob_pcm.c
index 4a55561ed..7a2c1f53b 100644
--- a/sound/firewire/bebob/bebob_pcm.c
+++ b/sound/firewire/bebob/bebob_pcm.c
@@ -157,7 +157,7 @@ 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;
+ enum snd_bebob_clock_type src;
int err;
err = snd_bebob_stream_lock_try(bebob);
@@ -168,7 +168,7 @@ pcm_open(struct snd_pcm_substream *substream)
if (err < 0)
goto err_locked;
- err = snd_bebob_stream_check_internal_clock(bebob, &internal);
+ err = snd_bebob_stream_get_clock_src(bebob, &src);
if (err < 0)
goto err_locked;
@@ -176,7 +176,7 @@ pcm_open(struct snd_pcm_substream *substream)
* When source of clock is internal or any PCM stream are running,
* the available sampling rate is limited at current sampling rate.
*/
- if (!internal ||
+ if (src == SND_BEBOB_CLOCK_TYPE_EXTERNAL ||
amdtp_stream_pcm_running(&bebob->tx_stream) ||
amdtp_stream_pcm_running(&bebob->rx_stream)) {
err = spec->get(bebob, &sampling_rate);
@@ -213,7 +213,7 @@ pcm_capture_hw_params(struct snd_pcm_substream *substream,
struct snd_bebob *bebob = substream->private_data;
if (substream->runtime->status->state == SNDRV_PCM_STATE_OPEN)
- atomic_inc(&bebob->capture_substreams);
+ atomic_inc(&bebob->substreams_counter);
amdtp_stream_set_pcm_format(&bebob->tx_stream,
params_format(hw_params));
return snd_pcm_lib_alloc_vmalloc_buffer(substream,
@@ -226,7 +226,7 @@ pcm_playback_hw_params(struct snd_pcm_substream *substream,
struct snd_bebob *bebob = substream->private_data;
if (substream->runtime->status->state == SNDRV_PCM_STATE_OPEN)
- atomic_inc(&bebob->playback_substreams);
+ atomic_inc(&bebob->substreams_counter);
amdtp_stream_set_pcm_format(&bebob->rx_stream,
params_format(hw_params));
return snd_pcm_lib_alloc_vmalloc_buffer(substream,
@@ -239,7 +239,7 @@ pcm_capture_hw_free(struct snd_pcm_substream *substream)
struct snd_bebob *bebob = substream->private_data;
if (substream->runtime->status->state != SNDRV_PCM_STATE_OPEN)
- atomic_dec(&bebob->capture_substreams);
+ atomic_dec(&bebob->substreams_counter);
snd_bebob_stream_stop_duplex(bebob);
@@ -251,7 +251,7 @@ pcm_playback_hw_free(struct snd_pcm_substream *substream)
struct snd_bebob *bebob = substream->private_data;
if (substream->runtime->status->state != SNDRV_PCM_STATE_OPEN)
- atomic_dec(&bebob->playback_substreams);
+ atomic_dec(&bebob->substreams_counter);
snd_bebob_stream_stop_duplex(bebob);
diff --git a/sound/firewire/bebob/bebob_proc.c b/sound/firewire/bebob/bebob_proc.c
index 335da6450..301cc6a93 100644
--- a/sound/firewire/bebob/bebob_proc.c
+++ b/sound/firewire/bebob/bebob_proc.c
@@ -132,25 +132,27 @@ static void
proc_read_clock(struct snd_info_entry *entry,
struct snd_info_buffer *buffer)
{
+ static const char *const clk_labels[] = {
+ "Internal",
+ "External",
+ "SYT-Match",
+ };
struct snd_bebob *bebob = entry->private_data;
struct snd_bebob_rate_spec *rate_spec = bebob->spec->rate;
struct snd_bebob_clock_spec *clk_spec = bebob->spec->clock;
- unsigned int rate, id;
- bool internal;
+ enum snd_bebob_clock_type src;
+ unsigned int rate;
if (rate_spec->get(bebob, &rate) >= 0)
snd_iprintf(buffer, "Sampling rate: %d\n", rate);
- if (clk_spec) {
- if (clk_spec->get(bebob, &id) >= 0)
+ if (snd_bebob_stream_get_clock_src(bebob, &src) >= 0) {
+ if (clk_spec)
snd_iprintf(buffer, "Clock Source: %s\n",
- clk_spec->labels[id]);
- } else {
- if (snd_bebob_stream_check_internal_clock(bebob,
- &internal) >= 0)
+ clk_labels[src]);
+ else
snd_iprintf(buffer, "Clock Source: %s (MSU-dest: %d)\n",
- (internal) ? "Internal" : "External",
- bebob->sync_input_plug);
+ clk_labels[src], bebob->sync_input_plug);
}
}
diff --git a/sound/firewire/bebob/bebob_stream.c b/sound/firewire/bebob/bebob_stream.c
index 98e4fc812..5be5242e1 100644
--- a/sound/firewire/bebob/bebob_stream.c
+++ b/sound/firewire/bebob/bebob_stream.c
@@ -8,7 +8,7 @@
#include "./bebob.h"
-#define CALLBACK_TIMEOUT 1000
+#define CALLBACK_TIMEOUT 2000
#define FW_ISO_RESOURCE_DELAY 1000
/*
@@ -116,16 +116,15 @@ end:
return err;
}
-int
-snd_bebob_stream_check_internal_clock(struct snd_bebob *bebob, bool *internal)
+int snd_bebob_stream_get_clock_src(struct snd_bebob *bebob,
+ enum snd_bebob_clock_type *src)
{
struct snd_bebob_clock_spec *clk_spec = bebob->spec->clock;
u8 addr[AVC_BRIDGECO_ADDR_BYTES], input[7];
unsigned int id;
+ enum avc_bridgeco_plug_type type;
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);
@@ -143,10 +142,7 @@ snd_bebob_stream_check_internal_clock(struct snd_bebob *bebob, bool *internal)
goto end;
}
- if (strncmp(clk_spec->labels[id], SND_BEBOB_CLOCK_INTERNAL,
- strlen(SND_BEBOB_CLOCK_INTERNAL)) == 0)
- *internal = true;
-
+ *src = clk_spec->types[id];
goto end;
}
@@ -155,7 +151,7 @@ snd_bebob_stream_check_internal_clock(struct snd_bebob *bebob, bool *internal)
* to use internal clock always
*/
if (bebob->sync_input_plug < 0) {
- *internal = true;
+ *src = SND_BEBOB_CLOCK_TYPE_INTERNAL;
goto end;
}
@@ -178,18 +174,79 @@ snd_bebob_stream_check_internal_clock(struct snd_bebob *bebob, bool *internal)
* Here check the first field. This field is used for direction.
*/
if (input[0] == 0xff) {
- *internal = true;
+ *src = SND_BEBOB_CLOCK_TYPE_INTERNAL;
goto end;
}
- /*
- * If source of clock is internal CSR, Music Sub Unit Sync Input is
- * a destination of Music Sub Unit Sync Output.
- */
- *internal = ((input[0] == AVC_BRIDGECO_PLUG_DIR_OUT) &&
- (input[1] == AVC_BRIDGECO_PLUG_MODE_SUBUNIT) &&
- (input[2] == 0x0c) &&
- (input[3] == 0x00));
+ /* The source from any output plugs is for one purpose only. */
+ if (input[0] == AVC_BRIDGECO_PLUG_DIR_OUT) {
+ /*
+ * In BeBoB architecture, the source from music subunit may
+ * bypass from oPCR[0]. This means that this source gives
+ * synchronization to IEEE 1394 cycle start packet.
+ */
+ if (input[1] == AVC_BRIDGECO_PLUG_MODE_SUBUNIT &&
+ input[2] == 0x0c) {
+ *src = SND_BEBOB_CLOCK_TYPE_INTERNAL;
+ goto end;
+ }
+ /* The source from any input units is for several purposes. */
+ } else if (input[1] == AVC_BRIDGECO_PLUG_MODE_UNIT) {
+ if (input[2] == AVC_BRIDGECO_PLUG_UNIT_ISOC) {
+ if (input[3] == 0x00) {
+ /*
+ * This source comes from iPCR[0]. This means
+ * that presentation timestamp calculated by
+ * SYT series of the received packets. In
+ * short, this driver is the master of
+ * synchronization.
+ */
+ *src = SND_BEBOB_CLOCK_TYPE_SYT;
+ goto end;
+ } else {
+ /*
+ * This source comes from iPCR[1-29]. This
+ * means that the synchronization stream is not
+ * the Audio/MIDI compound stream.
+ */
+ *src = SND_BEBOB_CLOCK_TYPE_EXTERNAL;
+ goto end;
+ }
+ } else if (input[2] == AVC_BRIDGECO_PLUG_UNIT_EXT) {
+ /* Check type of this plug. */
+ avc_bridgeco_fill_unit_addr(addr,
+ AVC_BRIDGECO_PLUG_DIR_IN,
+ AVC_BRIDGECO_PLUG_UNIT_EXT,
+ input[3]);
+ err = avc_bridgeco_get_plug_type(bebob->unit, addr,
+ &type);
+ if (err < 0)
+ goto end;
+
+ if (type == AVC_BRIDGECO_PLUG_TYPE_DIG) {
+ /*
+ * SPDIF/ADAT or sometimes (not always) word
+ * clock.
+ */
+ *src = SND_BEBOB_CLOCK_TYPE_EXTERNAL;
+ goto end;
+ } else if (type == AVC_BRIDGECO_PLUG_TYPE_SYNC) {
+ /* Often word clock. */
+ *src = SND_BEBOB_CLOCK_TYPE_EXTERNAL;
+ goto end;
+ } else if (type == AVC_BRIDGECO_PLUG_TYPE_ADDITION) {
+ /*
+ * Not standard.
+ * Mostly, additional internal clock.
+ */
+ *src = SND_BEBOB_CLOCK_TYPE_INTERNAL;
+ goto end;
+ }
+ }
+ }
+
+ /* Not supported. */
+ err = -EIO;
end:
return err;
}
@@ -417,8 +474,24 @@ destroy_both_connections(struct snd_bebob *bebob)
static int
get_sync_mode(struct snd_bebob *bebob, enum cip_flags *sync_mode)
{
- /* currently this module doesn't support SYT-Match mode */
- *sync_mode = CIP_SYNC_TO_DEVICE;
+ enum snd_bebob_clock_type src;
+ int err;
+
+ err = snd_bebob_stream_get_clock_src(bebob, &src);
+ if (err < 0)
+ return err;
+
+ switch (src) {
+ case SND_BEBOB_CLOCK_TYPE_INTERNAL:
+ case SND_BEBOB_CLOCK_TYPE_EXTERNAL:
+ *sync_mode = CIP_SYNC_TO_DEVICE;
+ break;
+ default:
+ case SND_BEBOB_CLOCK_TYPE_SYT:
+ *sync_mode = 0;
+ break;
+ }
+
return 0;
}
@@ -467,6 +540,17 @@ int snd_bebob_stream_init_duplex(struct snd_bebob *bebob)
/* See comments in next function */
init_completion(&bebob->bus_reset);
bebob->tx_stream.flags |= CIP_SKIP_INIT_DBC_CHECK;
+
+ /*
+ * BeBoB v3 transfers packets with these qurks:
+ * - In the beginning of streaming, the value of dbc is incremented
+ * even if no data blocks are transferred.
+ * - The value of dbc is reset suddenly.
+ */
+ if (bebob->version > 2)
+ bebob->tx_stream.flags |= CIP_EMPTY_HAS_WRONG_DBC |
+ CIP_SKIP_DBC_ZERO_CHECK;
+
/*
* At high sampling rate, M-Audio special firmware transmits empty
* packet with the value of dbc incremented by 8 but the others are
@@ -490,7 +574,6 @@ int snd_bebob_stream_start_duplex(struct snd_bebob *bebob, unsigned int rate)
{
struct snd_bebob_rate_spec *rate_spec = bebob->spec->rate;
struct amdtp_stream *master, *slave;
- atomic_t *slave_substreams;
enum cip_flags sync_mode;
unsigned int curr_rate;
bool updated = false;
@@ -515,8 +598,7 @@ int snd_bebob_stream_start_duplex(struct snd_bebob *bebob, unsigned int rate)
mutex_lock(&bebob->mutex);
/* Need no substreams */
- if (atomic_read(&bebob->playback_substreams) == 0 &&
- atomic_read(&bebob->capture_substreams) == 0)
+ if (atomic_read(&bebob->substreams_counter) == 0)
goto end;
err = get_sync_mode(bebob, &sync_mode);
@@ -525,11 +607,9 @@ int snd_bebob_stream_start_duplex(struct snd_bebob *bebob, unsigned int rate)
if (sync_mode == CIP_SYNC_TO_DEVICE) {
master = &bebob->tx_stream;
slave = &bebob->rx_stream;
- slave_substreams = &bebob->playback_substreams;
} else {
master = &bebob->rx_stream;
slave = &bebob->tx_stream;
- slave_substreams = &bebob->capture_substreams;
}
/*
@@ -630,7 +710,7 @@ int snd_bebob_stream_start_duplex(struct snd_bebob *bebob, unsigned int rate)
}
/* start slave if needed */
- if (atomic_read(slave_substreams) > 0 && !amdtp_stream_running(slave)) {
+ if (!amdtp_stream_running(slave)) {
err = start_stream(bebob, slave, rate);
if (err < 0) {
dev_err(&bebob->unit->device,
@@ -656,31 +736,25 @@ end:
void snd_bebob_stream_stop_duplex(struct snd_bebob *bebob)
{
struct amdtp_stream *master, *slave;
- atomic_t *master_substreams, *slave_substreams;
if (bebob->master == &bebob->rx_stream) {
slave = &bebob->tx_stream;
master = &bebob->rx_stream;
- slave_substreams = &bebob->capture_substreams;
- master_substreams = &bebob->playback_substreams;
} else {
slave = &bebob->rx_stream;
master = &bebob->tx_stream;
- slave_substreams = &bebob->playback_substreams;
- master_substreams = &bebob->capture_substreams;
}
mutex_lock(&bebob->mutex);
- if (atomic_read(slave_substreams) == 0) {
+ if (atomic_read(&bebob->substreams_counter) == 0) {
+ amdtp_stream_pcm_abort(master);
+ amdtp_stream_stop(master);
+
amdtp_stream_pcm_abort(slave);
amdtp_stream_stop(slave);
- if (atomic_read(master_substreams) == 0) {
- amdtp_stream_pcm_abort(master);
- amdtp_stream_stop(master);
- break_both_connections(bebob);
- }
+ break_both_connections(bebob);
}
mutex_unlock(&bebob->mutex);
diff --git a/sound/firewire/bebob/bebob_terratec.c b/sound/firewire/bebob/bebob_terratec.c
index ad635004d..9242e33d2 100644
--- a/sound/firewire/bebob/bebob_terratec.c
+++ b/sound/firewire/bebob/bebob_terratec.c
@@ -8,8 +8,10 @@
#include "./bebob.h"
-static const char *const phase88_rack_clk_src_labels[] = {
- SND_BEBOB_CLOCK_INTERNAL, "Digital In", "Word Clock"
+static enum snd_bebob_clock_type phase88_rack_clk_src_types[] = {
+ SND_BEBOB_CLOCK_TYPE_INTERNAL,
+ SND_BEBOB_CLOCK_TYPE_EXTERNAL, /* S/PDIF */
+ SND_BEBOB_CLOCK_TYPE_EXTERNAL, /* Word Clock */
};
static int
phase88_rack_clk_src_get(struct snd_bebob *bebob, unsigned int *id)
@@ -34,13 +36,23 @@ end:
return err;
}
-static const char *const phase24_series_clk_src_labels[] = {
- SND_BEBOB_CLOCK_INTERNAL, "Digital In"
+static enum snd_bebob_clock_type phase24_series_clk_src_types[] = {
+ SND_BEBOB_CLOCK_TYPE_INTERNAL,
+ SND_BEBOB_CLOCK_TYPE_EXTERNAL, /* S/PDIF */
};
static int
phase24_series_clk_src_get(struct snd_bebob *bebob, unsigned int *id)
{
- return avc_audio_get_selector(bebob->unit, 0, 4, id);
+ int err;
+
+ err = avc_audio_get_selector(bebob->unit, 0, 4, id);
+ if (err < 0)
+ return err;
+
+ if (*id >= ARRAY_SIZE(phase24_series_clk_src_types))
+ return -EIO;
+
+ return 0;
}
static struct snd_bebob_rate_spec phase_series_rate_spec = {
@@ -50,8 +62,8 @@ static struct snd_bebob_rate_spec phase_series_rate_spec = {
/* PHASE 88 Rack FW */
static struct snd_bebob_clock_spec phase88_rack_clk = {
- .num = ARRAY_SIZE(phase88_rack_clk_src_labels),
- .labels = phase88_rack_clk_src_labels,
+ .num = ARRAY_SIZE(phase88_rack_clk_src_types),
+ .types = phase88_rack_clk_src_types,
.get = &phase88_rack_clk_src_get,
};
struct snd_bebob_spec phase88_rack_spec = {
@@ -62,8 +74,8 @@ struct snd_bebob_spec phase88_rack_spec = {
/* 'PHASE 24 FW' and 'PHASE X24 FW' */
static struct snd_bebob_clock_spec phase24_series_clk = {
- .num = ARRAY_SIZE(phase24_series_clk_src_labels),
- .labels = phase24_series_clk_src_labels,
+ .num = ARRAY_SIZE(phase24_series_clk_src_types),
+ .types = phase24_series_clk_src_types,
.get = &phase24_series_clk_src_get,
};
struct snd_bebob_spec phase24_series_spec = {
diff --git a/sound/firewire/bebob/bebob_yamaha.c b/sound/firewire/bebob/bebob_yamaha.c
index ef1fe3823..581017024 100644
--- a/sound/firewire/bebob/bebob_yamaha.c
+++ b/sound/firewire/bebob/bebob_yamaha.c
@@ -28,15 +28,27 @@
* reccomend users to close ffado-mixer at 192.0kHz if mixer is needless.
*/
-static const char *const clk_src_labels[] = {SND_BEBOB_CLOCK_INTERNAL, "SPDIF"};
+static enum snd_bebob_clock_type clk_src_types[] = {
+ SND_BEBOB_CLOCK_TYPE_INTERNAL,
+ SND_BEBOB_CLOCK_TYPE_EXTERNAL, /* S/PDIF */
+};
static int
clk_src_get(struct snd_bebob *bebob, unsigned int *id)
{
- return avc_audio_get_selector(bebob->unit, 0, 4, id);
+ int err;
+
+ err = avc_audio_get_selector(bebob->unit, 0, 4, id);
+ if (err < 0)
+ return err;
+
+ if (*id >= ARRAY_SIZE(clk_src_types))
+ return -EIO;
+
+ return 0;
}
static struct snd_bebob_clock_spec clock_spec = {
- .num = ARRAY_SIZE(clk_src_labels),
- .labels = clk_src_labels,
+ .num = ARRAY_SIZE(clk_src_types),
+ .types = clk_src_types,
.get = &clk_src_get,
};
static struct snd_bebob_rate_spec rate_spec = {
diff --git a/sound/firewire/oxfw/oxfw-stream.c b/sound/firewire/oxfw/oxfw-stream.c
index e6757cd85..873d40fc4 100644
--- a/sound/firewire/oxfw/oxfw-stream.c
+++ b/sound/firewire/oxfw/oxfw-stream.c
@@ -232,9 +232,15 @@ int snd_oxfw_stream_init_simplex(struct snd_oxfw *oxfw,
goto end;
}
- /* OXFW starts to transmit packets with non-zero dbc. */
+ /*
+ * OXFW starts to transmit packets with non-zero dbc.
+ * OXFW postpone transferring packets till handling any asynchronous
+ * packets. As a result, next isochronous packet includes more data
+ * blocks than IEC 61883-6 defines.
+ */
if (stream == &oxfw->tx_stream)
- oxfw->tx_stream.flags |= CIP_SKIP_INIT_DBC_CHECK;
+ oxfw->tx_stream.flags |= CIP_SKIP_INIT_DBC_CHECK |
+ CIP_JUMBO_PAYLOAD;
end:
return err;
}