Merge branch 'for-next' into for-linus

Signed-off-by: Takashi Iwai <tiwai@suse.de>
This commit is contained in:
Takashi Iwai
2019-09-14 17:24:57 +02:00
13624 changed files with 1032172 additions and 456212 deletions

View File

@@ -146,19 +146,24 @@ void amdtp_am824_set_midi_position(struct amdtp_stream *s,
}
EXPORT_SYMBOL_GPL(amdtp_am824_set_midi_position);
static void write_pcm_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,
unsigned int pcm_frames)
{
struct amdtp_am824 *p = s->protocol;
unsigned int channels = p->pcm_channels;
struct snd_pcm_runtime *runtime = pcm->runtime;
unsigned int channels, remaining_frames, i, c;
unsigned int pcm_buffer_pointer;
int remaining_frames;
const u32 *src;
int i, c;
pcm_buffer_pointer = s->pcm_buffer_pointer + pcm_frames;
pcm_buffer_pointer %= runtime->buffer_size;
channels = p->pcm_channels;
src = (void *)runtime->dma_area +
frames_to_bytes(runtime, s->pcm_buffer_pointer);
remaining_frames = runtime->buffer_size - s->pcm_buffer_pointer;
frames_to_bytes(runtime, pcm_buffer_pointer);
remaining_frames = runtime->buffer_size - pcm_buffer_pointer;
for (i = 0; i < frames; ++i) {
for (c = 0; c < channels; ++c) {
@@ -172,19 +177,24 @@ static void write_pcm_s32(struct amdtp_stream *s,
}
}
static void read_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,
unsigned int pcm_frames)
{
struct amdtp_am824 *p = s->protocol;
unsigned int channels = p->pcm_channels;
struct snd_pcm_runtime *runtime = pcm->runtime;
unsigned int channels, remaining_frames, i, c;
unsigned int pcm_buffer_pointer;
int remaining_frames;
u32 *dst;
int i, c;
pcm_buffer_pointer = s->pcm_buffer_pointer + pcm_frames;
pcm_buffer_pointer %= runtime->buffer_size;
channels = p->pcm_channels;
dst = (void *)runtime->dma_area +
frames_to_bytes(runtime, s->pcm_buffer_pointer);
remaining_frames = runtime->buffer_size - s->pcm_buffer_pointer;
frames_to_bytes(runtime, pcm_buffer_pointer);
remaining_frames = runtime->buffer_size - pcm_buffer_pointer;
for (i = 0; i < frames; ++i) {
for (c = 0; c < channels; ++c) {
@@ -284,7 +294,7 @@ static void midi_rate_use_one_byte(struct amdtp_stream *s, unsigned int port)
}
static void write_midi_messages(struct amdtp_stream *s, __be32 *buffer,
unsigned int frames)
unsigned int frames, unsigned int data_block_counter)
{
struct amdtp_am824 *p = s->protocol;
unsigned int f, port;
@@ -293,7 +303,7 @@ static void write_midi_messages(struct amdtp_stream *s, __be32 *buffer,
for (f = 0; f < frames; f++) {
b = (u8 *)&buffer[p->midi_position];
port = (s->data_block_counter + f) % 8;
port = (data_block_counter + f) % 8;
if (f < MAX_MIDI_RX_BLOCKS &&
midi_ratelimit_per_packet(s, port) &&
p->midi[port] != NULL &&
@@ -311,16 +321,20 @@ static void write_midi_messages(struct amdtp_stream *s, __be32 *buffer,
}
}
static void read_midi_messages(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 data_block_counter)
{
struct amdtp_am824 *p = s->protocol;
unsigned int f, port;
int len;
u8 *b;
int f;
for (f = 0; f < frames; f++) {
port = (8 - s->ctx_data.tx.first_dbc + s->data_block_counter + f) % 8;
unsigned int port = f;
if (!(s->flags & CIP_UNALIGHED_DBC))
port += data_block_counter;
port %= 8;
b = (u8 *)&buffer[p->midi_position];
len = b[0] - 0x80;
@@ -331,44 +345,61 @@ static void read_midi_messages(struct amdtp_stream *s,
}
}
static unsigned int process_rx_data_blocks(struct amdtp_stream *s, __be32 *buffer,
unsigned int data_blocks, unsigned int *syt)
static unsigned int process_it_ctx_payloads(struct amdtp_stream *s,
const struct pkt_desc *descs,
unsigned int packets,
struct snd_pcm_substream *pcm)
{
struct amdtp_am824 *p = s->protocol;
struct snd_pcm_substream *pcm = READ_ONCE(s->pcm);
unsigned int pcm_frames;
unsigned int pcm_frames = 0;
int i;
if (pcm) {
write_pcm_s32(s, pcm, buffer, data_blocks);
pcm_frames = data_blocks * p->frame_multiplier;
} else {
write_pcm_silence(s, buffer, data_blocks);
pcm_frames = 0;
for (i = 0; i < packets; ++i) {
const struct pkt_desc *desc = descs + i;
__be32 *buf = desc->ctx_payload;
unsigned int data_blocks = desc->data_blocks;
if (pcm) {
write_pcm_s32(s, pcm, buf, data_blocks, pcm_frames);
pcm_frames += data_blocks * p->frame_multiplier;
} else {
write_pcm_silence(s, buf, data_blocks);
}
if (p->midi_ports) {
write_midi_messages(s, buf, data_blocks,
desc->data_block_counter);
}
}
if (p->midi_ports)
write_midi_messages(s, buffer, data_blocks);
return pcm_frames;
}
static unsigned int process_tx_data_blocks(struct amdtp_stream *s, __be32 *buffer,
unsigned int data_blocks, unsigned int *syt)
static unsigned int process_ir_ctx_payloads(struct amdtp_stream *s,
const struct pkt_desc *descs,
unsigned int packets,
struct snd_pcm_substream *pcm)
{
struct amdtp_am824 *p = s->protocol;
struct snd_pcm_substream *pcm = READ_ONCE(s->pcm);
unsigned int pcm_frames;
unsigned int pcm_frames = 0;
int i;
if (pcm) {
read_pcm_s32(s, pcm, buffer, data_blocks);
pcm_frames = data_blocks * p->frame_multiplier;
} else {
pcm_frames = 0;
for (i = 0; i < packets; ++i) {
const struct pkt_desc *desc = descs + i;
__be32 *buf = desc->ctx_payload;
unsigned int data_blocks = desc->data_blocks;
if (pcm) {
read_pcm_s32(s, pcm, buf, data_blocks, pcm_frames);
pcm_frames += data_blocks * p->frame_multiplier;
}
if (p->midi_ports) {
read_midi_messages(s, buf, data_blocks,
desc->data_block_counter);
}
}
if (p->midi_ports)
read_midi_messages(s, buffer, data_blocks);
return pcm_frames;
}
@@ -383,15 +414,14 @@ static unsigned int process_tx_data_blocks(struct amdtp_stream *s, __be32 *buffe
int amdtp_am824_init(struct amdtp_stream *s, struct fw_unit *unit,
enum amdtp_stream_direction dir, enum cip_flags flags)
{
amdtp_stream_process_data_blocks_t process_data_blocks;
amdtp_stream_process_ctx_payloads_t process_ctx_payloads;
if (dir == AMDTP_IN_STREAM)
process_data_blocks = process_tx_data_blocks;
process_ctx_payloads = process_ir_ctx_payloads;
else
process_data_blocks = process_rx_data_blocks;
process_ctx_payloads = process_it_ctx_payloads;
return amdtp_stream_init(s, unit, dir, flags, CIP_FMT_AM,
process_data_blocks,
sizeof(struct amdtp_am824));
process_ctx_payloads, sizeof(struct amdtp_am824));
}
EXPORT_SYMBOL_GPL(amdtp_am824_init);

View File

@@ -14,8 +14,8 @@
#include <linux/tracepoint.h>
TRACE_EVENT(amdtp_packet,
TP_PROTO(const struct amdtp_stream *s, u32 cycles, const __be32 *cip_header, unsigned int payload_length, unsigned int data_blocks, unsigned int index),
TP_ARGS(s, cycles, cip_header, payload_length, data_blocks, index),
TP_PROTO(const struct amdtp_stream *s, u32 cycles, const __be32 *cip_header, unsigned int payload_length, unsigned int data_blocks, unsigned int data_block_counter, unsigned int index),
TP_ARGS(s, cycles, cip_header, payload_length, data_blocks, data_block_counter, index),
TP_STRUCT__entry(
__field(unsigned int, second)
__field(unsigned int, cycle)
@@ -47,7 +47,7 @@ TRACE_EVENT(amdtp_packet,
}
__entry->payload_quadlets = payload_length / sizeof(__be32);
__entry->data_blocks = data_blocks;
__entry->data_block_counter = s->data_block_counter,
__entry->data_block_counter = data_block_counter,
__entry->packet_index = s->packet_index;
__entry->irq = !!in_interrupt();
__entry->index = index;

View File

@@ -74,16 +74,16 @@ static void pcm_period_tasklet(unsigned long data);
* @dir: the direction of stream
* @flags: the packet transmission method to use
* @fmt: the value of fmt field in CIP header
* @process_data_blocks: callback handler to process data blocks
* @process_ctx_payloads: callback handler to process payloads of isoc context
* @protocol_size: the size to allocate newly for protocol
*/
int amdtp_stream_init(struct amdtp_stream *s, struct fw_unit *unit,
enum amdtp_stream_direction dir, enum cip_flags flags,
unsigned int fmt,
amdtp_stream_process_data_blocks_t process_data_blocks,
amdtp_stream_process_ctx_payloads_t process_ctx_payloads,
unsigned int protocol_size)
{
if (process_data_blocks == NULL)
if (process_ctx_payloads == NULL)
return -EINVAL;
s->protocol = kzalloc(protocol_size, GFP_KERNEL);
@@ -102,7 +102,10 @@ int amdtp_stream_init(struct amdtp_stream *s, struct fw_unit *unit,
s->callbacked = false;
s->fmt = fmt;
s->process_data_blocks = process_data_blocks;
s->process_ctx_payloads = process_ctx_payloads;
if (dir == AMDTP_OUT_STREAM)
s->ctx_data.rx.syt_override = -1;
return 0;
}
@@ -473,12 +476,12 @@ static inline int queue_in_packet(struct amdtp_stream *s,
}
static void generate_cip_header(struct amdtp_stream *s, __be32 cip_header[2],
unsigned int syt)
unsigned int data_block_counter, unsigned int syt)
{
cip_header[0] = cpu_to_be32(READ_ONCE(s->source_node_id_field) |
(s->data_block_quadlets << CIP_DBS_SHIFT) |
((s->sph << CIP_SPH_SHIFT) & CIP_SPH_MASK) |
s->data_block_counter);
data_block_counter);
cip_header[1] = cpu_to_be32(CIP_EOH |
((s->fmt << CIP_FMT_SHIFT) & CIP_FMT_MASK) |
((s->ctx_data.rx.fdf << CIP_FDF_SHIFT) & CIP_FDF_MASK) |
@@ -487,8 +490,9 @@ static void generate_cip_header(struct amdtp_stream *s, __be32 cip_header[2],
static void build_it_pkt_header(struct amdtp_stream *s, unsigned int cycle,
struct fw_iso_packet *params,
unsigned int data_blocks, unsigned int syt,
unsigned int index)
unsigned int data_blocks,
unsigned int data_block_counter,
unsigned int syt, unsigned int index)
{
unsigned int payload_length;
__be32 *cip_header;
@@ -496,14 +500,9 @@ static void build_it_pkt_header(struct amdtp_stream *s, unsigned int cycle,
payload_length = data_blocks * sizeof(__be32) * s->data_block_quadlets;
params->payload_length = payload_length;
if (s->flags & CIP_DBC_IS_END_EVENT) {
s->data_block_counter =
(s->data_block_counter + data_blocks) & 0xff;
}
if (!(s->flags & CIP_NO_HEADER)) {
cip_header = (__be32 *)params->header;
generate_cip_header(s, cip_header, syt);
generate_cip_header(s, cip_header, data_block_counter, syt);
params->header_length = 2 * sizeof(__be32);
payload_length += params->header_length;
} else {
@@ -511,23 +510,19 @@ static void build_it_pkt_header(struct amdtp_stream *s, unsigned int cycle,
}
trace_amdtp_packet(s, cycle, cip_header, payload_length, data_blocks,
index);
if (!(s->flags & CIP_DBC_IS_END_EVENT)) {
s->data_block_counter =
(s->data_block_counter + data_blocks) & 0xff;
}
data_block_counter, index);
}
static int check_cip_header(struct amdtp_stream *s, const __be32 *buf,
unsigned int payload_length,
unsigned int *data_blocks, unsigned int *dbc,
unsigned int *syt)
unsigned int *data_blocks,
unsigned int *data_block_counter, unsigned int *syt)
{
u32 cip_header[2];
unsigned int sph;
unsigned int fmt;
unsigned int fdf;
unsigned int dbc;
bool lost;
cip_header[0] = be32_to_cpu(buf[0]);
@@ -579,17 +574,16 @@ static int check_cip_header(struct amdtp_stream *s, const __be32 *buf,
}
/* Check data block counter continuity */
*dbc = cip_header[0] & CIP_DBC_MASK;
dbc = cip_header[0] & CIP_DBC_MASK;
if (*data_blocks == 0 && (s->flags & CIP_EMPTY_HAS_WRONG_DBC) &&
s->data_block_counter != UINT_MAX)
*dbc = s->data_block_counter;
*data_block_counter != UINT_MAX)
dbc = *data_block_counter;
if (((s->flags & CIP_SKIP_DBC_ZERO_CHECK) &&
*dbc == s->ctx_data.tx.first_dbc) ||
s->data_block_counter == UINT_MAX) {
if ((dbc == 0x00 && (s->flags & CIP_SKIP_DBC_ZERO_CHECK)) ||
*data_block_counter == UINT_MAX) {
lost = false;
} else if (!(s->flags & CIP_DBC_IS_END_EVENT)) {
lost = *dbc != s->data_block_counter;
lost = dbc != *data_block_counter;
} else {
unsigned int dbc_interval;
@@ -598,16 +592,18 @@ static int check_cip_header(struct amdtp_stream *s, const __be32 *buf,
else
dbc_interval = *data_blocks;
lost = *dbc != ((s->data_block_counter + dbc_interval) & 0xff);
lost = dbc != ((*data_block_counter + dbc_interval) & 0xff);
}
if (lost) {
dev_err(&s->unit->device,
"Detect discontinuity of CIP: %02X %02X\n",
s->data_block_counter, *dbc);
*data_block_counter, dbc);
return -EIO;
}
*data_block_counter = dbc;
*syt = cip_header[1] & CIP_SYT_MASK;
return 0;
@@ -616,10 +612,10 @@ static int check_cip_header(struct amdtp_stream *s, const __be32 *buf,
static int parse_ir_ctx_header(struct amdtp_stream *s, unsigned int cycle,
const __be32 *ctx_header,
unsigned int *payload_length,
unsigned int *data_blocks, unsigned int *syt,
unsigned int index)
unsigned int *data_blocks,
unsigned int *data_block_counter,
unsigned int *syt, unsigned int index)
{
unsigned int dbc;
const __be32 *cip_header;
int err;
@@ -635,7 +631,7 @@ static int parse_ir_ctx_header(struct amdtp_stream *s, unsigned int cycle,
if (!(s->flags & CIP_NO_HEADER)) {
cip_header = ctx_header + 2;
err = check_cip_header(s, cip_header, *payload_length,
data_blocks, &dbc, syt);
data_blocks, data_block_counter, syt);
if (err < 0)
return err;
} else {
@@ -645,16 +641,12 @@ static int parse_ir_ctx_header(struct amdtp_stream *s, unsigned int cycle,
s->data_block_quadlets;
*syt = 0;
if (s->data_block_counter != UINT_MAX)
dbc = s->data_block_counter;
else
dbc = 0;
if (*data_block_counter == UINT_MAX)
*data_block_counter = 0;
}
s->data_block_counter = dbc;
trace_amdtp_packet(s, cycle, cip_header, *payload_length, *data_blocks,
index);
*data_block_counter, index);
return err;
}
@@ -686,6 +678,80 @@ static inline u32 compute_it_cycle(const __be32 ctx_header_tstamp)
return increment_cycle_count(cycle, QUEUE_LENGTH);
}
static int generate_device_pkt_descs(struct amdtp_stream *s,
struct pkt_desc *descs,
const __be32 *ctx_header,
unsigned int packets)
{
unsigned int dbc = s->data_block_counter;
int i;
int err;
for (i = 0; i < packets; ++i) {
struct pkt_desc *desc = descs + i;
unsigned int index = (s->packet_index + i) % QUEUE_LENGTH;
unsigned int cycle;
unsigned int payload_length;
unsigned int data_blocks;
unsigned int syt;
cycle = compute_cycle_count(ctx_header[1]);
err = parse_ir_ctx_header(s, cycle, ctx_header, &payload_length,
&data_blocks, &dbc, &syt, i);
if (err < 0)
return err;
desc->cycle = cycle;
desc->syt = syt;
desc->data_blocks = data_blocks;
desc->data_block_counter = dbc;
desc->ctx_payload = s->buffer.packets[index].buffer;
if (!(s->flags & CIP_DBC_IS_END_EVENT))
dbc = (dbc + desc->data_blocks) & 0xff;
ctx_header +=
s->ctx_data.tx.ctx_header_size / sizeof(*ctx_header);
}
s->data_block_counter = dbc;
return 0;
}
static void generate_ideal_pkt_descs(struct amdtp_stream *s,
struct pkt_desc *descs,
const __be32 *ctx_header,
unsigned int packets)
{
unsigned int dbc = s->data_block_counter;
int i;
for (i = 0; i < packets; ++i) {
struct pkt_desc *desc = descs + i;
unsigned int index = (s->packet_index + i) % QUEUE_LENGTH;
desc->cycle = compute_it_cycle(*ctx_header);
desc->syt = calculate_syt(s, desc->cycle);
desc->data_blocks = calculate_data_blocks(s, desc->syt);
if (s->flags & CIP_DBC_IS_END_EVENT)
dbc = (dbc + desc->data_blocks) & 0xff;
desc->data_block_counter = dbc;
if (!(s->flags & CIP_DBC_IS_END_EVENT))
dbc = (dbc + desc->data_blocks) & 0xff;
desc->ctx_payload = s->buffer.packets[index].buffer;
++ctx_header;
}
s->data_block_counter = dbc;
}
static inline void cancel_stream(struct amdtp_stream *s)
{
s->packet_index = -1;
@@ -694,6 +760,19 @@ static inline void cancel_stream(struct amdtp_stream *s)
WRITE_ONCE(s->pcm_buffer_pointer, SNDRV_PCM_POS_XRUN);
}
static void process_ctx_payloads(struct amdtp_stream *s,
const struct pkt_desc *descs,
unsigned int packets)
{
struct snd_pcm_substream *pcm;
unsigned int pcm_frames;
pcm = READ_ONCE(s->pcm);
pcm_frames = s->process_ctx_payloads(s, descs, packets, pcm);
if (pcm)
update_pcm_pointers(s, pcm, pcm_frames);
}
static void out_stream_callback(struct fw_iso_context *context, u32 tstamp,
size_t header_length, void *header,
void *private_data)
@@ -706,38 +785,31 @@ static void out_stream_callback(struct fw_iso_context *context, u32 tstamp,
if (s->packet_index < 0)
return;
generate_ideal_pkt_descs(s, s->pkt_descs, ctx_header, packets);
process_ctx_payloads(s, s->pkt_descs, packets);
for (i = 0; i < packets; ++i) {
u32 cycle;
const struct pkt_desc *desc = s->pkt_descs + i;
unsigned int syt;
unsigned int data_blocks;
__be32 *buffer;
unsigned int pcm_frames;
struct {
struct fw_iso_packet params;
__be32 header[IT_PKT_HEADER_SIZE_CIP / sizeof(__be32)];
} template = { {0}, {0} };
struct snd_pcm_substream *pcm;
cycle = compute_it_cycle(*ctx_header);
syt = calculate_syt(s, cycle);
data_blocks = calculate_data_blocks(s, syt);
buffer = s->buffer.packets[s->packet_index].buffer;
pcm_frames = s->process_data_blocks(s, buffer, data_blocks,
&syt);
if (s->ctx_data.rx.syt_override < 0)
syt = desc->syt;
else
syt = s->ctx_data.rx.syt_override;
build_it_pkt_header(s, cycle, &template.params, data_blocks,
build_it_pkt_header(s, desc->cycle, &template.params,
desc->data_blocks, desc->data_block_counter,
syt, i);
if (queue_out_packet(s, &template.params) < 0) {
cancel_stream(s);
return;
}
pcm = READ_ONCE(s->pcm);
if (pcm && pcm_frames > 0)
update_pcm_pointers(s, pcm, pcm_frames);
++ctx_header;
}
fw_iso_context_queue_flush(s->context);
@@ -748,8 +820,10 @@ static void in_stream_callback(struct fw_iso_context *context, u32 tstamp,
void *private_data)
{
struct amdtp_stream *s = private_data;
unsigned int i, packets;
unsigned int packets;
__be32 *ctx_header = header;
int i;
int err;
if (s->packet_index < 0)
return;
@@ -757,48 +831,23 @@ static void in_stream_callback(struct fw_iso_context *context, u32 tstamp,
// The number of packets in buffer.
packets = header_length / s->ctx_data.tx.ctx_header_size;
for (i = 0; i < packets; i++) {
u32 cycle;
unsigned int payload_length;
unsigned int data_blocks;
unsigned int syt;
__be32 *buffer;
unsigned int pcm_frames = 0;
struct fw_iso_packet params = {0};
struct snd_pcm_substream *pcm;
int err;
cycle = compute_cycle_count(ctx_header[1]);
err = parse_ir_ctx_header(s, cycle, ctx_header, &payload_length,
&data_blocks, &syt, i);
if (err < 0 && err != -EAGAIN)
break;
if (err >= 0) {
buffer = s->buffer.packets[s->packet_index].buffer;
pcm_frames = s->process_data_blocks(s, buffer,
data_blocks, &syt);
if (!(s->flags & CIP_DBC_IS_END_EVENT)) {
s->data_block_counter += data_blocks;
s->data_block_counter &= 0xff;
}
err = generate_device_pkt_descs(s, s->pkt_descs, ctx_header, packets);
if (err < 0) {
if (err != -EAGAIN) {
cancel_stream(s);
return;
}
if (queue_in_packet(s, &params) < 0)
break;
pcm = READ_ONCE(s->pcm);
if (pcm && pcm_frames > 0)
update_pcm_pointers(s, pcm, pcm_frames);
ctx_header += s->ctx_data.tx.ctx_header_size / sizeof(*ctx_header);
} else {
process_ctx_payloads(s, s->pkt_descs, packets);
}
/* Queueing error or detecting invalid payload. */
if (i < packets) {
cancel_stream(s);
return;
for (i = 0; i < packets; ++i) {
struct fw_iso_packet params = {0};
if (queue_in_packet(s, &params) < 0) {
cancel_stream(s);
return;
}
}
fw_iso_context_queue_flush(s->context);
@@ -845,7 +894,7 @@ static void amdtp_stream_first_callback(struct fw_iso_context *context,
* amdtp_stream_set_parameters() and it must be started before any PCM or MIDI
* device can be started.
*/
int amdtp_stream_start(struct amdtp_stream *s, int channel, int speed)
static int amdtp_stream_start(struct amdtp_stream *s, int channel, int speed)
{
static const struct {
unsigned int data_block;
@@ -932,6 +981,13 @@ int amdtp_stream_start(struct amdtp_stream *s, int channel, int speed)
else
s->tag = TAG_CIP;
s->pkt_descs = kcalloc(INTERRUPT_INTERVAL, sizeof(*s->pkt_descs),
GFP_KERNEL);
if (!s->pkt_descs) {
err = -ENOMEM;
goto err_context;
}
s->packet_index = 0;
do {
struct fw_iso_packet params;
@@ -943,7 +999,7 @@ int amdtp_stream_start(struct amdtp_stream *s, int channel, int speed)
err = queue_out_packet(s, &params);
}
if (err < 0)
goto err_context;
goto err_pkt_descs;
} while (s->packet_index > 0);
/* NOTE: TAG1 matches CIP. This just affects in stream. */
@@ -954,12 +1010,13 @@ int amdtp_stream_start(struct amdtp_stream *s, int channel, int speed)
s->callbacked = false;
err = fw_iso_context_start(s->context, -1, 0, tag);
if (err < 0)
goto err_context;
goto err_pkt_descs;
mutex_unlock(&s->mutex);
return 0;
err_pkt_descs:
kfree(s->pkt_descs);
err_context:
fw_iso_context_destroy(s->context);
s->context = ERR_PTR(-1);
@@ -970,7 +1027,6 @@ err_unlock:
return err;
}
EXPORT_SYMBOL(amdtp_stream_start);
/**
* amdtp_stream_pcm_pointer - get the PCM buffer position
@@ -1041,7 +1097,7 @@ EXPORT_SYMBOL(amdtp_stream_update);
* All PCM and MIDI devices of the stream must be stopped before the stream
* itself can be stopped.
*/
void amdtp_stream_stop(struct amdtp_stream *s)
static void amdtp_stream_stop(struct amdtp_stream *s)
{
mutex_lock(&s->mutex);
@@ -1055,12 +1111,12 @@ void amdtp_stream_stop(struct amdtp_stream *s)
fw_iso_context_destroy(s->context);
s->context = ERR_PTR(-1);
iso_packets_buffer_destroy(&s->buffer, s->unit);
kfree(s->pkt_descs);
s->callbacked = false;
mutex_unlock(&s->mutex);
}
EXPORT_SYMBOL(amdtp_stream_stop);
/**
* amdtp_stream_pcm_abort - abort the running PCM device
@@ -1078,3 +1134,92 @@ void amdtp_stream_pcm_abort(struct amdtp_stream *s)
snd_pcm_stop_xrun(pcm);
}
EXPORT_SYMBOL(amdtp_stream_pcm_abort);
/**
* amdtp_domain_init - initialize an AMDTP domain structure
* @d: the AMDTP domain to initialize.
*/
int amdtp_domain_init(struct amdtp_domain *d)
{
INIT_LIST_HEAD(&d->streams);
return 0;
}
EXPORT_SYMBOL_GPL(amdtp_domain_init);
/**
* amdtp_domain_destroy - destroy an AMDTP domain structure
* @d: the AMDTP domain to destroy.
*/
void amdtp_domain_destroy(struct amdtp_domain *d)
{
// At present nothing to do.
return;
}
EXPORT_SYMBOL_GPL(amdtp_domain_destroy);
/**
* amdtp_domain_add_stream - register isoc context into the domain.
* @d: the AMDTP domain.
* @s: the AMDTP stream.
* @channel: the isochronous channel on the bus.
* @speed: firewire speed code.
*/
int amdtp_domain_add_stream(struct amdtp_domain *d, struct amdtp_stream *s,
int channel, int speed)
{
struct amdtp_stream *tmp;
list_for_each_entry(tmp, &d->streams, list) {
if (s == tmp)
return -EBUSY;
}
list_add(&s->list, &d->streams);
s->channel = channel;
s->speed = speed;
return 0;
}
EXPORT_SYMBOL_GPL(amdtp_domain_add_stream);
/**
* amdtp_domain_start - start sending packets for isoc context in the domain.
* @d: the AMDTP domain.
*/
int amdtp_domain_start(struct amdtp_domain *d)
{
struct amdtp_stream *s;
int err = 0;
list_for_each_entry(s, &d->streams, list) {
err = amdtp_stream_start(s, s->channel, s->speed);
if (err < 0)
break;
}
if (err < 0) {
list_for_each_entry(s, &d->streams, list)
amdtp_stream_stop(s);
}
return err;
}
EXPORT_SYMBOL_GPL(amdtp_domain_start);
/**
* amdtp_domain_stop - stop sending packets for isoc context in the same domain.
* @d: the AMDTP domain to which the isoc contexts belong.
*/
void amdtp_domain_stop(struct amdtp_domain *d)
{
struct amdtp_stream *s, *next;
list_for_each_entry_safe(s, next, &d->streams, list) {
list_del(&s->list);
amdtp_stream_stop(s);
}
}
EXPORT_SYMBOL_GPL(amdtp_domain_stop);

View File

@@ -33,6 +33,8 @@
* @CIP_HEADER_WITHOUT_EOH: Only for in-stream. CIP Header doesn't include
* valid EOH.
* @CIP_NO_HEADERS: a lack of headers in packets
* @CIP_UNALIGHED_DBC: Only for in-stream. The value of dbc is not alighed to
* the value of current SYT_INTERVAL; e.g. initial value is not zero.
*/
enum cip_flags {
CIP_NONBLOCKING = 0x00,
@@ -45,6 +47,7 @@ enum cip_flags {
CIP_JUMBO_PAYLOAD = 0x40,
CIP_HEADER_WITHOUT_EOH = 0x80,
CIP_NO_HEADER = 0x100,
CIP_UNALIGHED_DBC = 0x200,
};
/**
@@ -91,12 +94,20 @@ enum amdtp_stream_direction {
AMDTP_IN_STREAM
};
struct pkt_desc {
u32 cycle;
u32 syt;
unsigned int data_blocks;
unsigned int data_block_counter;
__be32 *ctx_payload;
};
struct amdtp_stream;
typedef unsigned int (*amdtp_stream_process_data_blocks_t)(
typedef unsigned int (*amdtp_stream_process_ctx_payloads_t)(
struct amdtp_stream *s,
__be32 *buffer,
unsigned int data_blocks,
unsigned int *syt);
const struct pkt_desc *desc,
unsigned int packets,
struct snd_pcm_substream *pcm);
struct amdtp_stream {
struct fw_unit *unit;
enum cip_flags flags;
@@ -107,6 +118,7 @@ struct amdtp_stream {
struct fw_iso_context *context;
struct iso_packets_buffer buffer;
int packet_index;
struct pkt_desc *pkt_descs;
int tag;
union {
struct {
@@ -119,8 +131,6 @@ struct amdtp_stream {
// Fixed interval of dbc between previos/current
// packets.
unsigned int dbc_interval;
// Indicate the value of dbc field in a first packet.
unsigned int first_dbc;
} tx;
struct {
// To calculate CIP data blocks and tstamp.
@@ -131,6 +141,7 @@ struct amdtp_stream {
// To generate CIP header.
unsigned int fdf;
int syt_override;
} rx;
} ctx_data;
@@ -158,13 +169,18 @@ struct amdtp_stream {
/* For backends to process data blocks. */
void *protocol;
amdtp_stream_process_data_blocks_t process_data_blocks;
amdtp_stream_process_ctx_payloads_t process_ctx_payloads;
// For domain.
int channel;
int speed;
struct list_head list;
};
int amdtp_stream_init(struct amdtp_stream *s, struct fw_unit *unit,
enum amdtp_stream_direction dir, enum cip_flags flags,
unsigned int fmt,
amdtp_stream_process_data_blocks_t process_data_blocks,
amdtp_stream_process_ctx_payloads_t process_ctx_payloads,
unsigned int protocol_size);
void amdtp_stream_destroy(struct amdtp_stream *s);
@@ -172,9 +188,7 @@ int amdtp_stream_set_parameters(struct amdtp_stream *s, unsigned int rate,
unsigned int data_block_quadlets);
unsigned int amdtp_stream_get_max_payload(struct amdtp_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);
int amdtp_stream_add_pcm_hw_constraints(struct amdtp_stream *s,
struct snd_pcm_runtime *runtime);
@@ -256,4 +270,17 @@ static inline bool amdtp_stream_wait_callback(struct amdtp_stream *s,
msecs_to_jiffies(timeout)) > 0;
}
struct amdtp_domain {
struct list_head streams;
};
int amdtp_domain_init(struct amdtp_domain *d);
void amdtp_domain_destroy(struct amdtp_domain *d);
int amdtp_domain_add_stream(struct amdtp_domain *d, struct amdtp_stream *s,
int channel, int speed);
int amdtp_domain_start(struct amdtp_domain *d);
void amdtp_domain_stop(struct amdtp_domain *d);
#endif

View File

@@ -115,6 +115,8 @@ struct snd_bebob {
/* For BeBoB version quirk. */
unsigned int version;
struct amdtp_domain domain;
};
static inline int

View File

@@ -445,10 +445,9 @@ start_stream(struct snd_bebob *bebob, struct amdtp_stream *stream)
goto end;
}
/* start amdtp stream */
err = amdtp_stream_start(stream,
conn->resources.channel,
conn->speed);
// start amdtp stream.
err = amdtp_domain_add_stream(&bebob->domain, stream,
conn->resources.channel, conn->speed);
end:
return err;
}
@@ -523,7 +522,13 @@ int snd_bebob_stream_init_duplex(struct snd_bebob *bebob)
return err;
}
return 0;
err = amdtp_domain_init(&bebob->domain);
if (err < 0) {
destroy_stream(bebob, &bebob->tx_stream);
destroy_stream(bebob, &bebob->rx_stream);
}
return err;
}
static int keep_resources(struct snd_bebob *bebob, struct amdtp_stream *stream,
@@ -566,9 +571,7 @@ int snd_bebob_stream_reserve_duplex(struct snd_bebob *bebob, unsigned int rate)
if (rate == 0)
rate = curr_rate;
if (curr_rate != rate) {
amdtp_stream_stop(&bebob->tx_stream);
amdtp_stream_stop(&bebob->rx_stream);
amdtp_domain_stop(&bebob->domain);
break_both_connections(bebob);
cmp_connection_release(&bebob->out_conn);
@@ -620,9 +623,7 @@ int snd_bebob_stream_start_duplex(struct snd_bebob *bebob)
// packet queueing error or detecting discontinuity
if (amdtp_streaming_error(&bebob->rx_stream) ||
amdtp_streaming_error(&bebob->tx_stream)) {
amdtp_stream_stop(&bebob->rx_stream);
amdtp_stream_stop(&bebob->tx_stream);
amdtp_domain_stop(&bebob->domain);
break_both_connections(bebob);
}
@@ -640,11 +641,16 @@ int snd_bebob_stream_start_duplex(struct snd_bebob *bebob)
return err;
err = start_stream(bebob, &bebob->rx_stream);
if (err < 0) {
dev_err(&bebob->unit->device,
"fail to run AMDTP master stream:%d\n", err);
if (err < 0)
goto error;
err = start_stream(bebob, &bebob->tx_stream);
if (err < 0)
goto error;
err = amdtp_domain_start(&bebob->domain);
if (err < 0)
goto error;
}
// NOTE:
// The firmware customized by M-Audio uses these commands to
@@ -660,21 +666,8 @@ int snd_bebob_stream_start_duplex(struct snd_bebob *bebob)
}
if (!amdtp_stream_wait_callback(&bebob->rx_stream,
CALLBACK_TIMEOUT)) {
err = -ETIMEDOUT;
goto error;
}
}
if (!amdtp_stream_running(&bebob->tx_stream)) {
err = start_stream(bebob, &bebob->tx_stream);
if (err < 0) {
dev_err(&bebob->unit->device,
"fail to run AMDTP slave stream:%d\n", err);
goto error;
}
if (!amdtp_stream_wait_callback(&bebob->tx_stream,
CALLBACK_TIMEOUT) ||
!amdtp_stream_wait_callback(&bebob->tx_stream,
CALLBACK_TIMEOUT)) {
err = -ETIMEDOUT;
goto error;
@@ -683,8 +676,7 @@ int snd_bebob_stream_start_duplex(struct snd_bebob *bebob)
return 0;
error:
amdtp_stream_stop(&bebob->tx_stream);
amdtp_stream_stop(&bebob->rx_stream);
amdtp_domain_stop(&bebob->domain);
break_both_connections(bebob);
return err;
}
@@ -692,9 +684,7 @@ error:
void snd_bebob_stream_stop_duplex(struct snd_bebob *bebob)
{
if (bebob->substreams_counter == 0) {
amdtp_stream_stop(&bebob->rx_stream);
amdtp_stream_stop(&bebob->tx_stream);
amdtp_domain_stop(&bebob->domain);
break_both_connections(bebob);
cmp_connection_release(&bebob->out_conn);
@@ -708,6 +698,8 @@ void snd_bebob_stream_stop_duplex(struct snd_bebob *bebob)
*/
void snd_bebob_stream_destroy_duplex(struct snd_bebob *bebob)
{
amdtp_domain_destroy(&bebob->domain);
destroy_stream(bebob, &bebob->tx_stream);
destroy_stream(bebob, &bebob->rx_stream);
}

View File

@@ -154,14 +154,10 @@ static void stop_streams(struct snd_dice *dice, enum amdtp_stream_direction dir,
for (i = 0; i < params->count; i++) {
reg = cpu_to_be32((u32)-1);
if (dir == AMDTP_IN_STREAM) {
amdtp_stream_stop(&dice->tx_stream[i]);
snd_dice_transaction_write_tx(dice,
params->size * i + TX_ISOCHRONOUS,
&reg, sizeof(reg));
} else {
amdtp_stream_stop(&dice->rx_stream[i]);
snd_dice_transaction_write_rx(dice,
params->size * i + RX_ISOCHRONOUS,
&reg, sizeof(reg));
@@ -297,10 +293,11 @@ int snd_dice_stream_reserve_duplex(struct snd_dice *dice, unsigned int rate)
if (dice->substreams_counter == 0 || curr_rate != rate) {
struct reg_params tx_params, rx_params;
amdtp_domain_stop(&dice->domain);
err = get_register_params(dice, &tx_params, &rx_params);
if (err < 0)
return err;
finish_session(dice, &tx_params, &rx_params);
release_resources(dice);
@@ -377,7 +374,8 @@ static int start_streams(struct snd_dice *dice, enum amdtp_stream_direction dir,
return err;
}
err = amdtp_stream_start(stream, resources->channel, max_speed);
err = amdtp_domain_add_stream(&dice->domain, stream,
resources->channel, max_speed);
if (err < 0)
return err;
}
@@ -410,6 +408,7 @@ int snd_dice_stream_start_duplex(struct snd_dice *dice)
for (i = 0; i < MAX_STREAMS; ++i) {
if (amdtp_streaming_error(&dice->tx_stream[i]) ||
amdtp_streaming_error(&dice->rx_stream[i])) {
amdtp_domain_stop(&dice->domain);
finish_session(dice, &tx_params, &rx_params);
break;
}
@@ -456,6 +455,10 @@ int snd_dice_stream_start_duplex(struct snd_dice *dice)
goto error;
}
err = amdtp_domain_start(&dice->domain);
if (err < 0)
goto error;
for (i = 0; i < MAX_STREAMS; i++) {
if ((i < tx_params.count &&
!amdtp_stream_wait_callback(&dice->tx_stream[i],
@@ -471,6 +474,7 @@ int snd_dice_stream_start_duplex(struct snd_dice *dice)
return 0;
error:
amdtp_domain_stop(&dice->domain);
finish_session(dice, &tx_params, &rx_params);
return err;
}
@@ -485,8 +489,10 @@ void snd_dice_stream_stop_duplex(struct snd_dice *dice)
struct reg_params tx_params, rx_params;
if (dice->substreams_counter == 0) {
if (get_register_params(dice, &tx_params, &rx_params) >= 0)
if (get_register_params(dice, &tx_params, &rx_params) >= 0) {
amdtp_domain_stop(&dice->domain);
finish_session(dice, &tx_params, &rx_params);
}
release_resources(dice);
}
@@ -564,7 +570,15 @@ int snd_dice_stream_init_duplex(struct snd_dice *dice)
destroy_stream(dice, AMDTP_OUT_STREAM, i);
for (i = 0; i < MAX_STREAMS; i++)
destroy_stream(dice, AMDTP_IN_STREAM, i);
break;
goto end;
}
}
err = amdtp_domain_init(&dice->domain);
if (err < 0) {
for (i = 0; i < MAX_STREAMS; ++i) {
destroy_stream(dice, AMDTP_OUT_STREAM, i);
destroy_stream(dice, AMDTP_IN_STREAM, i);
}
}
end:
@@ -579,6 +593,8 @@ void snd_dice_stream_destroy_duplex(struct snd_dice *dice)
destroy_stream(dice, AMDTP_IN_STREAM, i);
destroy_stream(dice, AMDTP_OUT_STREAM, i);
}
amdtp_domain_destroy(&dice->domain);
}
void snd_dice_stream_update_duplex(struct snd_dice *dice)
@@ -596,6 +612,8 @@ void snd_dice_stream_update_duplex(struct snd_dice *dice)
dice->global_enabled = false;
if (get_register_params(dice, &tx_params, &rx_params) == 0) {
amdtp_domain_stop(&dice->domain);
stop_streams(dice, AMDTP_IN_STREAM, &tx_params);
stop_streams(dice, AMDTP_OUT_STREAM, &rx_params);
}

View File

@@ -112,6 +112,8 @@ struct snd_dice {
bool global_enabled;
struct completion clock_accepted;
unsigned int substreams_counter;
struct amdtp_domain domain;
};
enum snd_dice_addr_type {

View File

@@ -143,17 +143,23 @@ int amdtp_dot_set_parameters(struct amdtp_stream *s, unsigned int rate,
}
static void write_pcm_s32(struct amdtp_stream *s, struct snd_pcm_substream *pcm,
__be32 *buffer, unsigned int frames)
__be32 *buffer, unsigned int frames,
unsigned int pcm_frames)
{
struct amdtp_dot *p = s->protocol;
unsigned int channels = p->pcm_channels;
struct snd_pcm_runtime *runtime = pcm->runtime;
unsigned int channels, remaining_frames, i, c;
unsigned int pcm_buffer_pointer;
int remaining_frames;
const u32 *src;
int i, c;
pcm_buffer_pointer = s->pcm_buffer_pointer + pcm_frames;
pcm_buffer_pointer %= runtime->buffer_size;
channels = p->pcm_channels;
src = (void *)runtime->dma_area +
frames_to_bytes(runtime, s->pcm_buffer_pointer);
remaining_frames = runtime->buffer_size - s->pcm_buffer_pointer;
frames_to_bytes(runtime, pcm_buffer_pointer);
remaining_frames = runtime->buffer_size - pcm_buffer_pointer;
buffer++;
for (i = 0; i < frames; ++i) {
@@ -169,17 +175,23 @@ static void write_pcm_s32(struct amdtp_stream *s, struct snd_pcm_substream *pcm,
}
static void read_pcm_s32(struct amdtp_stream *s, struct snd_pcm_substream *pcm,
__be32 *buffer, unsigned int frames)
__be32 *buffer, unsigned int frames,
unsigned int pcm_frames)
{
struct amdtp_dot *p = s->protocol;
unsigned int channels = p->pcm_channels;
struct snd_pcm_runtime *runtime = pcm->runtime;
unsigned int channels, remaining_frames, i, c;
unsigned int pcm_buffer_pointer;
int remaining_frames;
u32 *dst;
int i, c;
pcm_buffer_pointer = s->pcm_buffer_pointer + pcm_frames;
pcm_buffer_pointer %= runtime->buffer_size;
channels = p->pcm_channels;
dst = (void *)runtime->dma_area +
frames_to_bytes(runtime, s->pcm_buffer_pointer);
remaining_frames = runtime->buffer_size - s->pcm_buffer_pointer;
frames_to_bytes(runtime, pcm_buffer_pointer);
remaining_frames = runtime->buffer_size - pcm_buffer_pointer;
buffer++;
for (i = 0; i < frames; ++i) {
@@ -234,7 +246,7 @@ static inline void midi_use_bytes(struct amdtp_stream *s,
}
static void write_midi_messages(struct amdtp_stream *s, __be32 *buffer,
unsigned int data_blocks)
unsigned int data_blocks, unsigned int data_block_counter)
{
struct amdtp_dot *p = s->protocol;
unsigned int f, port;
@@ -242,7 +254,7 @@ static void write_midi_messages(struct amdtp_stream *s, __be32 *buffer,
u8 *b;
for (f = 0; f < data_blocks; f++) {
port = (s->data_block_counter + f) % 8;
port = (data_block_counter + f) % 8;
b = (u8 *)&buffer[0];
len = 0;
@@ -329,66 +341,74 @@ void amdtp_dot_midi_trigger(struct amdtp_stream *s, unsigned int port,
WRITE_ONCE(p->midi[port], midi);
}
static unsigned int process_tx_data_blocks(struct amdtp_stream *s,
__be32 *buffer,
unsigned int data_blocks,
unsigned int *syt)
static unsigned int process_ir_ctx_payloads(struct amdtp_stream *s,
const struct pkt_desc *descs,
unsigned int packets,
struct snd_pcm_substream *pcm)
{
struct snd_pcm_substream *pcm;
unsigned int pcm_frames;
unsigned int pcm_frames = 0;
int i;
pcm = READ_ONCE(s->pcm);
if (pcm) {
read_pcm_s32(s, pcm, buffer, data_blocks);
pcm_frames = data_blocks;
} else {
pcm_frames = 0;
for (i = 0; i < packets; ++i) {
const struct pkt_desc *desc = descs + i;
__be32 *buf = desc->ctx_payload;
unsigned int data_blocks = desc->data_blocks;
if (pcm) {
read_pcm_s32(s, pcm, buf, data_blocks, pcm_frames);
pcm_frames += data_blocks;
}
read_midi_messages(s, buf, data_blocks);
}
read_midi_messages(s, buffer, data_blocks);
return pcm_frames;
}
static unsigned int process_rx_data_blocks(struct amdtp_stream *s,
__be32 *buffer,
unsigned int data_blocks,
unsigned int *syt)
static unsigned int process_it_ctx_payloads(struct amdtp_stream *s,
const struct pkt_desc *descs,
unsigned int packets,
struct snd_pcm_substream *pcm)
{
struct snd_pcm_substream *pcm;
unsigned int pcm_frames;
unsigned int pcm_frames = 0;
int i;
pcm = READ_ONCE(s->pcm);
if (pcm) {
write_pcm_s32(s, pcm, buffer, data_blocks);
pcm_frames = data_blocks;
} else {
write_pcm_silence(s, buffer, data_blocks);
pcm_frames = 0;
for (i = 0; i < packets; ++i) {
const struct pkt_desc *desc = descs + i;
__be32 *buf = desc->ctx_payload;
unsigned int data_blocks = desc->data_blocks;
if (pcm) {
write_pcm_s32(s, pcm, buf, data_blocks, pcm_frames);
pcm_frames += data_blocks;
} else {
write_pcm_silence(s, buf, data_blocks);
}
write_midi_messages(s, buf, data_blocks,
desc->data_block_counter);
}
write_midi_messages(s, buffer, data_blocks);
return pcm_frames;
}
int amdtp_dot_init(struct amdtp_stream *s, struct fw_unit *unit,
enum amdtp_stream_direction dir)
{
amdtp_stream_process_data_blocks_t process_data_blocks;
amdtp_stream_process_ctx_payloads_t process_ctx_payloads;
enum cip_flags flags;
/* Use different mode between incoming/outgoing. */
// Use different mode between incoming/outgoing.
if (dir == AMDTP_IN_STREAM) {
flags = CIP_NONBLOCKING;
process_data_blocks = process_tx_data_blocks;
process_ctx_payloads = process_ir_ctx_payloads;
} else {
flags = CIP_BLOCKING;
process_data_blocks = process_rx_data_blocks;
process_ctx_payloads = process_it_ctx_payloads;
}
return amdtp_stream_init(s, unit, dir, flags, CIP_FMT_AM,
process_data_blocks, sizeof(struct amdtp_dot));
process_ctx_payloads, sizeof(struct amdtp_dot));
}
void amdtp_dot_reset(struct amdtp_stream *s)

View File

@@ -126,9 +126,6 @@ static void finish_session(struct snd_dg00x *dg00x)
{
__be32 data;
amdtp_stream_stop(&dg00x->tx_stream);
amdtp_stream_stop(&dg00x->rx_stream);
data = cpu_to_be32(0x00000003);
snd_fw_transaction(dg00x->unit, TCODE_WRITE_QUADLET_REQUEST,
DG00X_ADDR_BASE + DG00X_OFFSET_STREAMING_SET,
@@ -218,29 +215,59 @@ static int keep_resources(struct snd_dg00x *dg00x, struct amdtp_stream *stream,
fw_parent_device(dg00x->unit)->max_speed);
}
static int init_stream(struct snd_dg00x *dg00x, struct amdtp_stream *s)
{
struct fw_iso_resources *resources;
enum amdtp_stream_direction dir;
int err;
if (s == &dg00x->tx_stream) {
resources = &dg00x->tx_resources;
dir = AMDTP_IN_STREAM;
} else {
resources = &dg00x->rx_resources;
dir = AMDTP_OUT_STREAM;
}
err = fw_iso_resources_init(resources, dg00x->unit);
if (err < 0)
return err;
err = amdtp_dot_init(s, dg00x->unit, dir);
if (err < 0)
fw_iso_resources_destroy(resources);
return err;
}
static void destroy_stream(struct snd_dg00x *dg00x, struct amdtp_stream *s)
{
amdtp_stream_destroy(s);
if (s == &dg00x->tx_stream)
fw_iso_resources_destroy(&dg00x->tx_resources);
else
fw_iso_resources_destroy(&dg00x->rx_resources);
}
int snd_dg00x_stream_init_duplex(struct snd_dg00x *dg00x)
{
int err;
/* For out-stream. */
err = fw_iso_resources_init(&dg00x->rx_resources, dg00x->unit);
err = init_stream(dg00x, &dg00x->rx_stream);
if (err < 0)
goto error;
err = amdtp_dot_init(&dg00x->rx_stream, dg00x->unit, AMDTP_OUT_STREAM);
if (err < 0)
goto error;
return err;
/* For in-stream. */
err = fw_iso_resources_init(&dg00x->tx_resources, dg00x->unit);
err = init_stream(dg00x, &dg00x->tx_stream);
if (err < 0)
goto error;
err = amdtp_dot_init(&dg00x->tx_stream, dg00x->unit, AMDTP_IN_STREAM);
if (err < 0)
goto error;
destroy_stream(dg00x, &dg00x->rx_stream);
err = amdtp_domain_init(&dg00x->domain);
if (err < 0) {
destroy_stream(dg00x, &dg00x->rx_stream);
destroy_stream(dg00x, &dg00x->tx_stream);
}
return 0;
error:
snd_dg00x_stream_destroy_duplex(dg00x);
return err;
}
@@ -250,11 +277,10 @@ error:
*/
void snd_dg00x_stream_destroy_duplex(struct snd_dg00x *dg00x)
{
amdtp_stream_destroy(&dg00x->rx_stream);
fw_iso_resources_destroy(&dg00x->rx_resources);
amdtp_domain_destroy(&dg00x->domain);
amdtp_stream_destroy(&dg00x->tx_stream);
fw_iso_resources_destroy(&dg00x->tx_resources);
destroy_stream(dg00x, &dg00x->rx_stream);
destroy_stream(dg00x, &dg00x->tx_stream);
}
int snd_dg00x_stream_reserve_duplex(struct snd_dg00x *dg00x, unsigned int rate)
@@ -269,6 +295,8 @@ int snd_dg00x_stream_reserve_duplex(struct snd_dg00x *dg00x, unsigned int rate)
rate = curr_rate;
if (dg00x->substreams_counter == 0 || curr_rate != rate) {
amdtp_domain_stop(&dg00x->domain);
finish_session(dg00x);
fw_iso_resources_free(&dg00x->tx_resources);
@@ -301,8 +329,10 @@ int snd_dg00x_stream_start_duplex(struct snd_dg00x *dg00x)
return 0;
if (amdtp_streaming_error(&dg00x->tx_stream) ||
amdtp_streaming_error(&dg00x->rx_stream))
amdtp_streaming_error(&dg00x->rx_stream)) {
amdtp_domain_stop(&dg00x->domain);
finish_session(dg00x);
}
if (generation != fw_parent_device(dg00x->unit)->card->generation) {
err = fw_iso_resources_update(&dg00x->tx_resources);
@@ -319,36 +349,30 @@ int snd_dg00x_stream_start_duplex(struct snd_dg00x *dg00x)
* which source of clock is used.
*/
if (!amdtp_stream_running(&dg00x->rx_stream)) {
int spd = fw_parent_device(dg00x->unit)->max_speed;
err = begin_session(dg00x);
if (err < 0)
goto error;
err = amdtp_stream_start(&dg00x->rx_stream,
dg00x->rx_resources.channel,
fw_parent_device(dg00x->unit)->max_speed);
err = amdtp_domain_add_stream(&dg00x->domain, &dg00x->rx_stream,
dg00x->rx_resources.channel, spd);
if (err < 0)
goto error;
err = amdtp_domain_add_stream(&dg00x->domain, &dg00x->tx_stream,
dg00x->tx_resources.channel, spd);
if (err < 0)
goto error;
err = amdtp_domain_start(&dg00x->domain);
if (err < 0)
goto error;
if (!amdtp_stream_wait_callback(&dg00x->rx_stream,
CALLBACK_TIMEOUT)) {
err = -ETIMEDOUT;
goto error;
}
}
/*
* The value of SYT field in transmitted packets is always 0x0000. Thus,
* duplex streams with timestamp synchronization cannot be built.
*/
if (!amdtp_stream_running(&dg00x->tx_stream)) {
err = amdtp_stream_start(&dg00x->tx_stream,
dg00x->tx_resources.channel,
fw_parent_device(dg00x->unit)->max_speed);
if (err < 0)
goto error;
if (!amdtp_stream_wait_callback(&dg00x->tx_stream,
CALLBACK_TIMEOUT)) {
CALLBACK_TIMEOUT) ||
!amdtp_stream_wait_callback(&dg00x->tx_stream,
CALLBACK_TIMEOUT)) {
err = -ETIMEDOUT;
goto error;
}
@@ -356,6 +380,7 @@ int snd_dg00x_stream_start_duplex(struct snd_dg00x *dg00x)
return 0;
error:
amdtp_domain_stop(&dg00x->domain);
finish_session(dg00x);
return err;
@@ -364,6 +389,7 @@ error:
void snd_dg00x_stream_stop_duplex(struct snd_dg00x *dg00x)
{
if (dg00x->substreams_counter == 0) {
amdtp_domain_stop(&dg00x->domain);
finish_session(dg00x);
fw_iso_resources_free(&dg00x->tx_resources);

View File

@@ -59,6 +59,8 @@ struct snd_dg00x {
/* Console models have additional MIDI ports for control surface. */
bool is_console;
struct amdtp_domain domain;
};
#define DG00X_ADDR_BASE 0xffffe0000000ull

View File

@@ -27,19 +27,24 @@ int amdtp_ff_set_parameters(struct amdtp_stream *s, unsigned int rate,
return amdtp_stream_set_parameters(s, rate, data_channels);
}
static void write_pcm_s32(struct amdtp_stream *s,
struct snd_pcm_substream *pcm,
__le32 *buffer, unsigned int frames)
static void write_pcm_s32(struct amdtp_stream *s, struct snd_pcm_substream *pcm,
__le32 *buffer, unsigned int frames,
unsigned int pcm_frames)
{
struct amdtp_ff *p = s->protocol;
unsigned int channels = p->pcm_channels;
struct snd_pcm_runtime *runtime = pcm->runtime;
unsigned int channels, remaining_frames, i, c;
unsigned int pcm_buffer_pointer;
int remaining_frames;
const u32 *src;
int i, c;
pcm_buffer_pointer = s->pcm_buffer_pointer + pcm_frames;
pcm_buffer_pointer %= runtime->buffer_size;
channels = p->pcm_channels;
src = (void *)runtime->dma_area +
frames_to_bytes(runtime, s->pcm_buffer_pointer);
remaining_frames = runtime->buffer_size - s->pcm_buffer_pointer;
frames_to_bytes(runtime, pcm_buffer_pointer);
remaining_frames = runtime->buffer_size - pcm_buffer_pointer;
for (i = 0; i < frames; ++i) {
for (c = 0; c < channels; ++c) {
@@ -52,19 +57,24 @@ static void write_pcm_s32(struct amdtp_stream *s,
}
}
static void read_pcm_s32(struct amdtp_stream *s,
struct snd_pcm_substream *pcm,
__le32 *buffer, unsigned int frames)
static void read_pcm_s32(struct amdtp_stream *s, struct snd_pcm_substream *pcm,
__le32 *buffer, unsigned int frames,
unsigned int pcm_frames)
{
struct amdtp_ff *p = s->protocol;
unsigned int channels = p->pcm_channels;
struct snd_pcm_runtime *runtime = pcm->runtime;
unsigned int channels, remaining_frames, i, c;
unsigned int pcm_buffer_pointer;
int remaining_frames;
u32 *dst;
int i, c;
pcm_buffer_pointer = s->pcm_buffer_pointer + pcm_frames;
pcm_buffer_pointer %= runtime->buffer_size;
channels = p->pcm_channels;
dst = (void *)runtime->dma_area +
frames_to_bytes(runtime, s->pcm_buffer_pointer);
remaining_frames = runtime->buffer_size - s->pcm_buffer_pointer;
frames_to_bytes(runtime, pcm_buffer_pointer);
remaining_frames = runtime->buffer_size - pcm_buffer_pointer;
for (i = 0; i < frames; ++i) {
for (c = 0; c < channels; ++c) {
@@ -102,38 +112,47 @@ int amdtp_ff_add_pcm_hw_constraints(struct amdtp_stream *s,
return amdtp_stream_add_pcm_hw_constraints(s, runtime);
}
static unsigned int process_rx_data_blocks(struct amdtp_stream *s,
__be32 *buffer,
unsigned int data_blocks,
unsigned int *syt)
static unsigned int process_it_ctx_payloads(struct amdtp_stream *s,
const struct pkt_desc *descs,
unsigned int packets,
struct snd_pcm_substream *pcm)
{
struct snd_pcm_substream *pcm = READ_ONCE(s->pcm);
unsigned int pcm_frames;
unsigned int pcm_frames = 0;
int i;
if (pcm) {
write_pcm_s32(s, pcm, (__le32 *)buffer, data_blocks);
pcm_frames = data_blocks;
} else {
write_pcm_silence(s, (__le32 *)buffer, data_blocks);
pcm_frames = 0;
for (i = 0; i < packets; ++i) {
const struct pkt_desc *desc = descs + i;
__le32 *buf = (__le32 *)desc->ctx_payload;
unsigned int data_blocks = desc->data_blocks;
if (pcm) {
write_pcm_s32(s, pcm, buf, data_blocks, pcm_frames);
pcm_frames += data_blocks;
} else {
write_pcm_silence(s, buf, data_blocks);
}
}
return pcm_frames;
}
static unsigned int process_tx_data_blocks(struct amdtp_stream *s,
__be32 *buffer,
unsigned int data_blocks,
unsigned int *syt)
static unsigned int process_ir_ctx_payloads(struct amdtp_stream *s,
const struct pkt_desc *descs,
unsigned int packets,
struct snd_pcm_substream *pcm)
{
struct snd_pcm_substream *pcm = READ_ONCE(s->pcm);
unsigned int pcm_frames;
unsigned int pcm_frames = 0;
int i;
if (pcm) {
read_pcm_s32(s, pcm, (__le32 *)buffer, data_blocks);
pcm_frames = data_blocks;
} else {
pcm_frames = 0;
for (i = 0; i < packets; ++i) {
const struct pkt_desc *desc = descs + i;
__le32 *buf = (__le32 *)desc->ctx_payload;
unsigned int data_blocks = desc->data_blocks;
if (pcm) {
read_pcm_s32(s, pcm, buf, data_blocks, pcm_frames);
pcm_frames += data_blocks;
}
}
return pcm_frames;
@@ -142,13 +161,13 @@ static unsigned int process_tx_data_blocks(struct amdtp_stream *s,
int amdtp_ff_init(struct amdtp_stream *s, struct fw_unit *unit,
enum amdtp_stream_direction dir)
{
amdtp_stream_process_data_blocks_t process_data_blocks;
amdtp_stream_process_ctx_payloads_t process_ctx_payloads;
if (dir == AMDTP_IN_STREAM)
process_data_blocks = process_tx_data_blocks;
process_ctx_payloads = process_ir_ctx_payloads;
else
process_data_blocks = process_rx_data_blocks;
process_ctx_payloads = process_it_ctx_payloads;
return amdtp_stream_init(s, unit, dir, CIP_NO_HEADER, 0,
process_data_blocks, sizeof(struct amdtp_ff));
process_ctx_payloads, sizeof(struct amdtp_ff));
}

View File

@@ -32,61 +32,65 @@ int snd_ff_stream_get_multiplier_mode(enum cip_sfc sfc,
static inline void finish_session(struct snd_ff *ff)
{
amdtp_stream_stop(&ff->tx_stream);
amdtp_stream_stop(&ff->rx_stream);
ff->spec->protocol->finish_session(ff);
ff->spec->protocol->switch_fetching_mode(ff, false);
}
static int init_stream(struct snd_ff *ff, enum amdtp_stream_direction dir)
static int init_stream(struct snd_ff *ff, struct amdtp_stream *s)
{
int err;
struct fw_iso_resources *resources;
struct amdtp_stream *stream;
enum amdtp_stream_direction dir;
int err;
if (dir == AMDTP_IN_STREAM) {
if (s == &ff->tx_stream) {
resources = &ff->tx_resources;
stream = &ff->tx_stream;
dir = AMDTP_IN_STREAM;
} else {
resources = &ff->rx_resources;
stream = &ff->rx_stream;
dir = AMDTP_OUT_STREAM;
}
err = fw_iso_resources_init(resources, ff->unit);
if (err < 0)
return err;
err = amdtp_ff_init(stream, ff->unit, dir);
err = amdtp_ff_init(s, ff->unit, dir);
if (err < 0)
fw_iso_resources_destroy(resources);
return err;
}
static void destroy_stream(struct snd_ff *ff, enum amdtp_stream_direction dir)
static void destroy_stream(struct snd_ff *ff, struct amdtp_stream *s)
{
if (dir == AMDTP_IN_STREAM) {
amdtp_stream_destroy(&ff->tx_stream);
amdtp_stream_destroy(s);
if (s == &ff->tx_stream)
fw_iso_resources_destroy(&ff->tx_resources);
} else {
amdtp_stream_destroy(&ff->rx_stream);
else
fw_iso_resources_destroy(&ff->rx_resources);
}
}
int snd_ff_stream_init_duplex(struct snd_ff *ff)
{
int err;
err = init_stream(ff, AMDTP_OUT_STREAM);
err = init_stream(ff, &ff->rx_stream);
if (err < 0)
goto end;
return err;
err = init_stream(ff, &ff->tx_stream);
if (err < 0) {
destroy_stream(ff, &ff->rx_stream);
return err;
}
err = amdtp_domain_init(&ff->domain);
if (err < 0) {
destroy_stream(ff, &ff->rx_stream);
destroy_stream(ff, &ff->tx_stream);
}
err = init_stream(ff, AMDTP_IN_STREAM);
if (err < 0)
destroy_stream(ff, AMDTP_OUT_STREAM);
end:
return err;
}
@@ -96,8 +100,10 @@ end:
*/
void snd_ff_stream_destroy_duplex(struct snd_ff *ff)
{
destroy_stream(ff, AMDTP_IN_STREAM);
destroy_stream(ff, AMDTP_OUT_STREAM);
amdtp_domain_destroy(&ff->domain);
destroy_stream(ff, &ff->rx_stream);
destroy_stream(ff, &ff->tx_stream);
}
int snd_ff_stream_reserve_duplex(struct snd_ff *ff, unsigned int rate)
@@ -114,6 +120,7 @@ int snd_ff_stream_reserve_duplex(struct snd_ff *ff, unsigned int rate)
enum snd_ff_stream_mode mode;
int i;
amdtp_domain_stop(&ff->domain);
finish_session(ff);
fw_iso_resources_free(&ff->tx_resources);
@@ -156,25 +163,39 @@ int snd_ff_stream_start_duplex(struct snd_ff *ff, unsigned int rate)
return 0;
if (amdtp_streaming_error(&ff->tx_stream) ||
amdtp_streaming_error(&ff->rx_stream))
amdtp_streaming_error(&ff->rx_stream)) {
amdtp_domain_stop(&ff->domain);
finish_session(ff);
}
/*
* Regardless of current source of clock signal, drivers transfer some
* packets. Then, the device transfers packets.
*/
if (!amdtp_stream_running(&ff->rx_stream)) {
int spd = fw_parent_device(ff->unit)->max_speed;
err = ff->spec->protocol->begin_session(ff, rate);
if (err < 0)
goto error;
err = amdtp_stream_start(&ff->rx_stream,
ff->rx_resources.channel,
fw_parent_device(ff->unit)->max_speed);
err = amdtp_domain_add_stream(&ff->domain, &ff->rx_stream,
ff->rx_resources.channel, spd);
if (err < 0)
goto error;
err = amdtp_domain_add_stream(&ff->domain, &ff->tx_stream,
ff->tx_resources.channel, spd);
if (err < 0)
goto error;
err = amdtp_domain_start(&ff->domain);
if (err < 0)
goto error;
if (!amdtp_stream_wait_callback(&ff->rx_stream,
CALLBACK_TIMEOUT_MS) ||
!amdtp_stream_wait_callback(&ff->tx_stream,
CALLBACK_TIMEOUT_MS)) {
err = -ETIMEDOUT;
goto error;
@@ -185,22 +206,9 @@ int snd_ff_stream_start_duplex(struct snd_ff *ff, unsigned int rate)
goto error;
}
if (!amdtp_stream_running(&ff->tx_stream)) {
err = amdtp_stream_start(&ff->tx_stream,
ff->tx_resources.channel,
fw_parent_device(ff->unit)->max_speed);
if (err < 0)
goto error;
if (!amdtp_stream_wait_callback(&ff->tx_stream,
CALLBACK_TIMEOUT_MS)) {
err = -ETIMEDOUT;
goto error;
}
}
return 0;
error:
amdtp_domain_stop(&ff->domain);
finish_session(ff);
return err;
@@ -209,6 +217,7 @@ error:
void snd_ff_stream_stop_duplex(struct snd_ff *ff)
{
if (ff->substreams_counter == 0) {
amdtp_domain_stop(&ff->domain);
finish_session(ff);
fw_iso_resources_free(&ff->tx_resources);
@@ -218,12 +227,11 @@ void snd_ff_stream_stop_duplex(struct snd_ff *ff)
void snd_ff_stream_update_duplex(struct snd_ff *ff)
{
amdtp_domain_stop(&ff->domain);
// The device discontinue to transfer packets.
amdtp_stream_pcm_abort(&ff->tx_stream);
amdtp_stream_stop(&ff->tx_stream);
amdtp_stream_pcm_abort(&ff->rx_stream);
amdtp_stream_stop(&ff->rx_stream);
}
void snd_ff_stream_lock_changed(struct snd_ff *ff)

View File

@@ -91,6 +91,8 @@ struct snd_ff {
int dev_lock_count;
bool dev_lock_changed;
wait_queue_head_t hwdep_wait;
struct amdtp_domain domain;
};
enum snd_ff_clock_src {

View File

@@ -107,6 +107,8 @@ struct snd_efw {
u8 *resp_buf;
u8 *pull_ptr;
u8 *push_ptr;
struct amdtp_domain domain;
};
int snd_efw_transaction_cmd(struct fw_unit *unit,

View File

@@ -8,8 +8,7 @@
#define CALLBACK_TIMEOUT 100
static int
init_stream(struct snd_efw *efw, struct amdtp_stream *stream)
static int init_stream(struct snd_efw *efw, struct amdtp_stream *stream)
{
struct cmp_connection *conn;
enum cmp_direction c_dir;
@@ -28,28 +27,40 @@ init_stream(struct snd_efw *efw, struct amdtp_stream *stream)
err = cmp_connection_init(conn, efw->unit, c_dir, 0);
if (err < 0)
goto end;
return err;
err = amdtp_am824_init(stream, efw->unit, s_dir, CIP_BLOCKING);
if (err < 0) {
amdtp_stream_destroy(stream);
cmp_connection_destroy(conn);
return err;
}
end:
if (stream == &efw->tx_stream) {
// Fireworks transmits NODATA packets with TAG0.
efw->tx_stream.flags |= CIP_EMPTY_WITH_TAG0;
// Fireworks has its own meaning for dbc.
efw->tx_stream.flags |= CIP_DBC_IS_END_EVENT;
// Fireworks reset dbc at bus reset.
efw->tx_stream.flags |= CIP_SKIP_DBC_ZERO_CHECK;
// But Recent firmwares starts packets with non-zero dbc.
// Driver version 5.7.6 installs firmware version 5.7.3.
if (efw->is_fireworks3 &&
(efw->firmware_version == 0x5070000 ||
efw->firmware_version == 0x5070300 ||
efw->firmware_version == 0x5080000))
efw->tx_stream.flags |= CIP_UNALIGHED_DBC;
// AudioFire9 always reports wrong dbs.
if (efw->is_af9)
efw->tx_stream.flags |= CIP_WRONG_DBS;
// Firmware version 5.5 reports fixed interval for dbc.
if (efw->firmware_version == 0x5050000)
efw->tx_stream.ctx_data.tx.dbc_interval = 8;
}
return err;
}
static void
stop_stream(struct snd_efw *efw, struct amdtp_stream *stream)
{
amdtp_stream_stop(stream);
if (stream == &efw->tx_stream)
cmp_connection_break(&efw->out_conn);
else
cmp_connection_break(&efw->in_conn);
}
static int start_stream(struct snd_efw *efw, struct amdtp_stream *stream,
unsigned int rate)
{
@@ -67,38 +78,26 @@ static int start_stream(struct snd_efw *efw, struct amdtp_stream *stream,
return err;
// Start amdtp stream.
err = amdtp_stream_start(stream, conn->resources.channel, conn->speed);
err = amdtp_domain_add_stream(&efw->domain, stream,
conn->resources.channel, conn->speed);
if (err < 0) {
cmp_connection_break(conn);
return err;
}
// Wait first callback.
if (!amdtp_stream_wait_callback(stream, CALLBACK_TIMEOUT)) {
amdtp_stream_stop(stream);
cmp_connection_break(conn);
return -ETIMEDOUT;
}
return 0;
}
/*
* This function should be called before starting the stream or after stopping
* the streams.
*/
static void
destroy_stream(struct snd_efw *efw, struct amdtp_stream *stream)
// This function should be called before starting the stream or after stopping
// the streams.
static void destroy_stream(struct snd_efw *efw, struct amdtp_stream *stream)
{
struct cmp_connection *conn;
amdtp_stream_destroy(stream);
if (stream == &efw->tx_stream)
conn = &efw->out_conn;
cmp_connection_destroy(&efw->out_conn);
else
conn = &efw->in_conn;
amdtp_stream_destroy(stream);
cmp_connection_destroy(conn);
cmp_connection_destroy(&efw->in_conn);
}
static int
@@ -131,42 +130,28 @@ int snd_efw_stream_init_duplex(struct snd_efw *efw)
err = init_stream(efw, &efw->tx_stream);
if (err < 0)
goto end;
/* Fireworks transmits NODATA packets with TAG0. */
efw->tx_stream.flags |= CIP_EMPTY_WITH_TAG0;
/* Fireworks has its own meaning for dbc. */
efw->tx_stream.flags |= CIP_DBC_IS_END_EVENT;
/* Fireworks reset dbc at bus reset. */
efw->tx_stream.flags |= CIP_SKIP_DBC_ZERO_CHECK;
/*
* But Recent firmwares starts packets with non-zero dbc.
* Driver version 5.7.6 installs firmware version 5.7.3.
*/
if (efw->is_fireworks3 &&
(efw->firmware_version == 0x5070000 ||
efw->firmware_version == 0x5070300 ||
efw->firmware_version == 0x5080000))
efw->tx_stream.ctx_data.tx.first_dbc = 0x02;
/* AudioFire9 always reports wrong dbs. */
if (efw->is_af9)
efw->tx_stream.flags |= CIP_WRONG_DBS;
/* Firmware version 5.5 reports fixed interval for dbc. */
if (efw->firmware_version == 0x5050000)
efw->tx_stream.ctx_data.tx.dbc_interval = 8;
return err;
err = init_stream(efw, &efw->rx_stream);
if (err < 0) {
destroy_stream(efw, &efw->tx_stream);
goto end;
return err;
}
/* set IEC61883 compliant mode (actually not fully compliant...) */
err = amdtp_domain_init(&efw->domain);
if (err < 0) {
destroy_stream(efw, &efw->tx_stream);
destroy_stream(efw, &efw->rx_stream);
return err;
}
// set IEC61883 compliant mode (actually not fully compliant...).
err = snd_efw_command_set_tx_mode(efw, SND_EFW_TRANSPORT_MODE_IEC61883);
if (err < 0) {
destroy_stream(efw, &efw->tx_stream);
destroy_stream(efw, &efw->rx_stream);
}
end:
return err;
}
@@ -214,8 +199,10 @@ int snd_efw_stream_reserve_duplex(struct snd_efw *efw, unsigned int rate)
if (rate == 0)
rate = curr_rate;
if (rate != curr_rate) {
stop_stream(efw, &efw->tx_stream);
stop_stream(efw, &efw->rx_stream);
amdtp_domain_stop(&efw->domain);
cmp_connection_break(&efw->out_conn);
cmp_connection_break(&efw->in_conn);
cmp_connection_release(&efw->out_conn);
cmp_connection_release(&efw->in_conn);
@@ -255,47 +242,57 @@ int snd_efw_stream_start_duplex(struct snd_efw *efw)
if (efw->substreams_counter == 0)
return -EIO;
if (amdtp_streaming_error(&efw->rx_stream) ||
amdtp_streaming_error(&efw->tx_stream)) {
amdtp_domain_stop(&efw->domain);
cmp_connection_break(&efw->out_conn);
cmp_connection_break(&efw->in_conn);
}
err = snd_efw_command_get_sampling_rate(efw, &rate);
if (err < 0)
return err;
if (amdtp_streaming_error(&efw->rx_stream) ||
amdtp_streaming_error(&efw->tx_stream)) {
stop_stream(efw, &efw->rx_stream);
stop_stream(efw, &efw->tx_stream);
}
/* master should be always running */
if (!amdtp_stream_running(&efw->rx_stream)) {
err = start_stream(efw, &efw->rx_stream, rate);
if (err < 0) {
dev_err(&efw->unit->device,
"fail to start AMDTP master stream:%d\n", err);
if (err < 0)
goto error;
}
}
if (!amdtp_stream_running(&efw->tx_stream)) {
err = start_stream(efw, &efw->tx_stream, rate);
if (err < 0) {
dev_err(&efw->unit->device,
"fail to start AMDTP slave stream:%d\n", err);
if (err < 0)
goto error;
err = amdtp_domain_start(&efw->domain);
if (err < 0)
goto error;
// Wait first callback.
if (!amdtp_stream_wait_callback(&efw->rx_stream,
CALLBACK_TIMEOUT) ||
!amdtp_stream_wait_callback(&efw->tx_stream,
CALLBACK_TIMEOUT)) {
err = -ETIMEDOUT;
goto error;
}
}
return 0;
error:
stop_stream(efw, &efw->rx_stream);
stop_stream(efw, &efw->tx_stream);
amdtp_domain_stop(&efw->domain);
cmp_connection_break(&efw->out_conn);
cmp_connection_break(&efw->in_conn);
return err;
}
void snd_efw_stream_stop_duplex(struct snd_efw *efw)
{
if (efw->substreams_counter == 0) {
stop_stream(efw, &efw->tx_stream);
stop_stream(efw, &efw->rx_stream);
amdtp_domain_stop(&efw->domain);
cmp_connection_break(&efw->out_conn);
cmp_connection_break(&efw->in_conn);
cmp_connection_release(&efw->out_conn);
cmp_connection_release(&efw->in_conn);
@@ -304,18 +301,19 @@ void snd_efw_stream_stop_duplex(struct snd_efw *efw)
void snd_efw_stream_update_duplex(struct snd_efw *efw)
{
if (cmp_connection_update(&efw->out_conn) < 0 ||
cmp_connection_update(&efw->in_conn) < 0) {
stop_stream(efw, &efw->rx_stream);
stop_stream(efw, &efw->tx_stream);
} else {
amdtp_stream_update(&efw->rx_stream);
amdtp_stream_update(&efw->tx_stream);
}
amdtp_domain_stop(&efw->domain);
cmp_connection_break(&efw->out_conn);
cmp_connection_break(&efw->in_conn);
amdtp_stream_pcm_abort(&efw->rx_stream);
amdtp_stream_pcm_abort(&efw->tx_stream);
}
void snd_efw_stream_destroy_duplex(struct snd_efw *efw)
{
amdtp_domain_destroy(&efw->domain);
destroy_stream(efw, &efw->rx_stream);
destroy_stream(efw, &efw->tx_stream);
}

View File

@@ -117,19 +117,25 @@ int amdtp_motu_set_parameters(struct amdtp_stream *s, unsigned int rate,
return 0;
}
static void read_pcm_s32(struct amdtp_stream *s,
struct snd_pcm_runtime *runtime,
__be32 *buffer, unsigned int data_blocks)
static void read_pcm_s32(struct amdtp_stream *s, struct snd_pcm_substream *pcm,
__be32 *buffer, unsigned int data_blocks,
unsigned int pcm_frames)
{
struct amdtp_motu *p = s->protocol;
unsigned int channels, remaining_frames, i, c;
unsigned int channels = p->pcm_chunks;
struct snd_pcm_runtime *runtime = pcm->runtime;
unsigned int pcm_buffer_pointer;
int remaining_frames;
u8 *byte;
u32 *dst;
int i, c;
pcm_buffer_pointer = s->pcm_buffer_pointer + pcm_frames;
pcm_buffer_pointer %= runtime->buffer_size;
channels = p->pcm_chunks;
dst = (void *)runtime->dma_area +
frames_to_bytes(runtime, s->pcm_buffer_pointer);
remaining_frames = runtime->buffer_size - s->pcm_buffer_pointer;
frames_to_bytes(runtime, pcm_buffer_pointer);
remaining_frames = runtime->buffer_size - pcm_buffer_pointer;
for (i = 0; i < data_blocks; ++i) {
byte = (u8 *)buffer + p->pcm_byte_offset;
@@ -147,19 +153,25 @@ static void read_pcm_s32(struct amdtp_stream *s,
}
}
static void write_pcm_s32(struct amdtp_stream *s,
struct snd_pcm_runtime *runtime,
__be32 *buffer, unsigned int data_blocks)
static void write_pcm_s32(struct amdtp_stream *s, struct snd_pcm_substream *pcm,
__be32 *buffer, unsigned int data_blocks,
unsigned int pcm_frames)
{
struct amdtp_motu *p = s->protocol;
unsigned int channels, remaining_frames, i, c;
unsigned int channels = p->pcm_chunks;
struct snd_pcm_runtime *runtime = pcm->runtime;
unsigned int pcm_buffer_pointer;
int remaining_frames;
u8 *byte;
const u32 *src;
int i, c;
pcm_buffer_pointer = s->pcm_buffer_pointer + pcm_frames;
pcm_buffer_pointer %= runtime->buffer_size;
channels = p->pcm_chunks;
src = (void *)runtime->dma_area +
frames_to_bytes(runtime, s->pcm_buffer_pointer);
remaining_frames = runtime->buffer_size - s->pcm_buffer_pointer;
frames_to_bytes(runtime, pcm_buffer_pointer);
remaining_frames = runtime->buffer_size - pcm_buffer_pointer;
for (i = 0; i < data_blocks; ++i) {
byte = (u8 *)buffer + p->pcm_byte_offset;
@@ -298,24 +310,52 @@ static void __maybe_unused copy_message(u64 *frames, __be32 *buffer,
}
}
static unsigned int process_tx_data_blocks(struct amdtp_stream *s,
__be32 *buffer, unsigned int data_blocks,
unsigned int *syt)
static void probe_tracepoints_events(struct amdtp_stream *s,
const struct pkt_desc *descs,
unsigned int packets)
{
int i;
for (i = 0; i < packets; ++i) {
const struct pkt_desc *desc = descs + i;
__be32 *buf = desc->ctx_payload;
unsigned int data_blocks = desc->data_blocks;
trace_data_block_sph(s, data_blocks, buf);
trace_data_block_message(s, data_blocks, buf);
}
}
static unsigned int process_ir_ctx_payloads(struct amdtp_stream *s,
const struct pkt_desc *descs,
unsigned int packets,
struct snd_pcm_substream *pcm)
{
struct amdtp_motu *p = s->protocol;
struct snd_pcm_substream *pcm;
unsigned int pcm_frames = 0;
int i;
trace_data_block_sph(s, data_blocks, buffer);
trace_data_block_message(s, data_blocks, buffer);
// For data block processing.
for (i = 0; i < packets; ++i) {
const struct pkt_desc *desc = descs + i;
__be32 *buf = desc->ctx_payload;
unsigned int data_blocks = desc->data_blocks;
if (p->midi_ports)
read_midi_messages(s, buffer, data_blocks);
if (pcm) {
read_pcm_s32(s, pcm, buf, data_blocks, pcm_frames);
pcm_frames += data_blocks;
}
pcm = READ_ONCE(s->pcm);
if (data_blocks > 0 && pcm)
read_pcm_s32(s, pcm->runtime, buffer, data_blocks);
if (p->midi_ports)
read_midi_messages(s, buf, data_blocks);
}
return data_blocks;
// For tracepoints.
if (trace_data_block_sph_enabled() ||
trace_data_block_message_enabled())
probe_tracepoints_events(s, descs, packets);
return pcm_frames;
}
static inline void compute_next_elapse_from_start(struct amdtp_motu *p)
@@ -360,46 +400,55 @@ static void write_sph(struct amdtp_stream *s, __be32 *buffer,
}
}
static unsigned int process_rx_data_blocks(struct amdtp_stream *s,
__be32 *buffer, unsigned int data_blocks,
unsigned int *syt)
static unsigned int process_it_ctx_payloads(struct amdtp_stream *s,
const struct pkt_desc *descs,
unsigned int packets,
struct snd_pcm_substream *pcm)
{
struct amdtp_motu *p = (struct amdtp_motu *)s->protocol;
struct snd_pcm_substream *pcm;
struct amdtp_motu *p = s->protocol;
unsigned int pcm_frames = 0;
int i;
/* Not used. */
*syt = 0xffff;
// For data block processing.
for (i = 0; i < packets; ++i) {
const struct pkt_desc *desc = descs + i;
__be32 *buf = desc->ctx_payload;
unsigned int data_blocks = desc->data_blocks;
/* TODO: how to interact control messages between userspace? */
if (pcm) {
write_pcm_s32(s, pcm, buf, data_blocks, pcm_frames);
pcm_frames += data_blocks;
} else {
write_pcm_silence(s, buf, data_blocks);
}
if (p->midi_ports)
write_midi_messages(s, buffer, data_blocks);
if (p->midi_ports)
write_midi_messages(s, buf, data_blocks);
pcm = READ_ONCE(s->pcm);
if (pcm)
write_pcm_s32(s, pcm->runtime, buffer, data_blocks);
else
write_pcm_silence(s, buffer, data_blocks);
// TODO: how to interact control messages between userspace?
write_sph(s, buffer, data_blocks);
write_sph(s, buf, data_blocks);
}
trace_data_block_sph(s, data_blocks, buffer);
trace_data_block_message(s, data_blocks, buffer);
// For tracepoints.
if (trace_data_block_sph_enabled() ||
trace_data_block_message_enabled())
probe_tracepoints_events(s, descs, packets);
return data_blocks;
return pcm_frames;
}
int amdtp_motu_init(struct amdtp_stream *s, struct fw_unit *unit,
enum amdtp_stream_direction dir,
const struct snd_motu_protocol *const protocol)
{
amdtp_stream_process_data_blocks_t process_data_blocks;
amdtp_stream_process_ctx_payloads_t process_ctx_payloads;
int fmt = CIP_FMT_MOTU;
int flags = CIP_BLOCKING;
int err;
if (dir == AMDTP_IN_STREAM) {
process_data_blocks = process_tx_data_blocks;
process_ctx_payloads = process_ir_ctx_payloads;
/*
* Units of version 3 transmits packets with invalid CIP header
@@ -418,17 +467,23 @@ int amdtp_motu_init(struct amdtp_stream *s, struct fw_unit *unit,
CIP_SKIP_DBC_ZERO_CHECK;
}
} else {
process_data_blocks = process_rx_data_blocks;
process_ctx_payloads = process_it_ctx_payloads;
flags |= CIP_DBC_IS_END_EVENT;
}
err = amdtp_stream_init(s, unit, dir, flags, fmt, process_data_blocks,
err = amdtp_stream_init(s, unit, dir, flags, fmt, process_ctx_payloads,
sizeof(struct amdtp_motu));
if (err < 0)
return err;
s->sph = 1;
s->ctx_data.rx.fdf = MOTU_FDF_AM824;
if (dir == AMDTP_OUT_STREAM) {
// Use fixed value for FDF field.
s->ctx_data.rx.fdf = MOTU_FDF_AM824;
// Not used.
s->ctx_data.rx.syt_override = 0xffff;
}
return 0;
}

View File

@@ -92,9 +92,6 @@ static void finish_session(struct snd_motu *motu)
if (err < 0)
return;
amdtp_stream_stop(&motu->tx_stream);
amdtp_stream_stop(&motu->rx_stream);
err = snd_motu_transaction_read(motu, ISOC_COMM_CONTROL_OFFSET, &reg,
sizeof(reg));
if (err < 0)
@@ -109,27 +106,6 @@ static void finish_session(struct snd_motu *motu)
sizeof(reg));
}
static int start_isoc_ctx(struct snd_motu *motu, struct amdtp_stream *stream)
{
struct fw_iso_resources *resources;
int err;
if (stream == &motu->rx_stream)
resources = &motu->rx_resources;
else
resources = &motu->tx_resources;
err = amdtp_stream_start(stream, resources->channel,
fw_parent_device(motu->unit)->max_speed);
if (err < 0)
return err;
if (!amdtp_stream_wait_callback(stream, CALLBACK_TIMEOUT))
return -ETIMEDOUT;
return 0;
}
int snd_motu_stream_cache_packet_formats(struct snd_motu *motu)
{
int err;
@@ -169,6 +145,7 @@ int snd_motu_stream_reserve_duplex(struct snd_motu *motu, unsigned int rate)
rate = curr_rate;
if (motu->substreams_counter == 0 || curr_rate != rate) {
amdtp_domain_stop(&motu->domain);
finish_session(motu);
fw_iso_resources_free(&motu->tx_resources);
@@ -234,8 +211,10 @@ int snd_motu_stream_start_duplex(struct snd_motu *motu)
return 0;
if (amdtp_streaming_error(&motu->rx_stream) ||
amdtp_streaming_error(&motu->tx_stream))
amdtp_streaming_error(&motu->tx_stream)) {
amdtp_domain_stop(&motu->domain);
finish_session(motu);
}
if (generation != fw_parent_device(motu->unit)->card->generation) {
err = fw_iso_resources_update(&motu->rx_resources);
@@ -248,6 +227,8 @@ int snd_motu_stream_start_duplex(struct snd_motu *motu)
}
if (!amdtp_stream_running(&motu->rx_stream)) {
int spd = fw_parent_device(motu->unit)->max_speed;
err = ensure_packet_formats(motu);
if (err < 0)
return err;
@@ -259,10 +240,25 @@ int snd_motu_stream_start_duplex(struct snd_motu *motu)
goto stop_streams;
}
err = start_isoc_ctx(motu, &motu->rx_stream);
if (err < 0) {
dev_err(&motu->unit->device,
"fail to start IT context: %d\n", err);
err = amdtp_domain_add_stream(&motu->domain, &motu->tx_stream,
motu->tx_resources.channel, spd);
if (err < 0)
goto stop_streams;
err = amdtp_domain_add_stream(&motu->domain, &motu->rx_stream,
motu->rx_resources.channel, spd);
if (err < 0)
goto stop_streams;
err = amdtp_domain_start(&motu->domain);
if (err < 0)
goto stop_streams;
if (!amdtp_stream_wait_callback(&motu->tx_stream,
CALLBACK_TIMEOUT) ||
!amdtp_stream_wait_callback(&motu->rx_stream,
CALLBACK_TIMEOUT)) {
err = -ETIMEDOUT;
goto stop_streams;
}
@@ -274,18 +270,10 @@ int snd_motu_stream_start_duplex(struct snd_motu *motu)
}
}
if (!amdtp_stream_running(&motu->tx_stream)) {
err = start_isoc_ctx(motu, &motu->tx_stream);
if (err < 0) {
dev_err(&motu->unit->device,
"fail to start IR context: %d", err);
goto stop_streams;
}
}
return 0;
stop_streams:
amdtp_domain_stop(&motu->domain);
finish_session(motu);
return err;
}
@@ -293,6 +281,7 @@ stop_streams:
void snd_motu_stream_stop_duplex(struct snd_motu *motu)
{
if (motu->substreams_counter == 0) {
amdtp_domain_stop(&motu->domain);
finish_session(motu);
fw_iso_resources_free(&motu->tx_resources);
@@ -300,74 +289,72 @@ void snd_motu_stream_stop_duplex(struct snd_motu *motu)
}
}
static int init_stream(struct snd_motu *motu, enum amdtp_stream_direction dir)
static int init_stream(struct snd_motu *motu, struct amdtp_stream *s)
{
int err;
struct amdtp_stream *stream;
struct fw_iso_resources *resources;
enum amdtp_stream_direction dir;
int err;
if (dir == AMDTP_IN_STREAM) {
stream = &motu->tx_stream;
if (s == &motu->tx_stream) {
resources = &motu->tx_resources;
dir = AMDTP_IN_STREAM;
} else {
stream = &motu->rx_stream;
resources = &motu->rx_resources;
dir = AMDTP_OUT_STREAM;
}
err = fw_iso_resources_init(resources, motu->unit);
if (err < 0)
return err;
err = amdtp_motu_init(stream, motu->unit, dir, motu->spec->protocol);
if (err < 0) {
amdtp_stream_destroy(stream);
err = amdtp_motu_init(s, motu->unit, dir, motu->spec->protocol);
if (err < 0)
fw_iso_resources_destroy(resources);
}
return err;
}
static void destroy_stream(struct snd_motu *motu,
enum amdtp_stream_direction dir)
static void destroy_stream(struct snd_motu *motu, struct amdtp_stream *s)
{
struct amdtp_stream *stream;
struct fw_iso_resources *resources;
amdtp_stream_destroy(s);
if (dir == AMDTP_IN_STREAM) {
stream = &motu->tx_stream;
resources = &motu->tx_resources;
} else {
stream = &motu->rx_stream;
resources = &motu->rx_resources;
}
amdtp_stream_destroy(stream);
fw_iso_resources_destroy(resources);
if (s == &motu->tx_stream)
fw_iso_resources_destroy(&motu->tx_resources);
else
fw_iso_resources_destroy(&motu->rx_resources);
}
int snd_motu_stream_init_duplex(struct snd_motu *motu)
{
int err;
err = init_stream(motu, AMDTP_IN_STREAM);
err = init_stream(motu, &motu->tx_stream);
if (err < 0)
return err;
err = init_stream(motu, AMDTP_OUT_STREAM);
if (err < 0)
destroy_stream(motu, AMDTP_IN_STREAM);
err = init_stream(motu, &motu->rx_stream);
if (err < 0) {
destroy_stream(motu, &motu->tx_stream);
return err;
}
err = amdtp_domain_init(&motu->domain);
if (err < 0) {
destroy_stream(motu, &motu->tx_stream);
destroy_stream(motu, &motu->rx_stream);
}
return err;
}
/*
* This function should be called before starting streams or after stopping
* streams.
*/
// This function should be called before starting streams or after stopping
// streams.
void snd_motu_stream_destroy_duplex(struct snd_motu *motu)
{
destroy_stream(motu, AMDTP_IN_STREAM);
destroy_stream(motu, AMDTP_OUT_STREAM);
amdtp_domain_destroy(&motu->domain);
destroy_stream(motu, &motu->rx_stream);
destroy_stream(motu, &motu->tx_stream);
motu->substreams_counter = 0;
}

View File

@@ -247,6 +247,17 @@ static const struct snd_motu_spec motu_audio_express = {
.analog_out_ports = 4,
};
static const struct snd_motu_spec motu_4pre = {
.name = "4pre",
.protocol = &snd_motu_protocol_v3,
.flags = SND_MOTU_SPEC_SUPPORT_CLOCK_X2 |
SND_MOTU_SPEC_TX_MICINST_CHUNK |
SND_MOTU_SPEC_TX_RETURN_CHUNK |
SND_MOTU_SPEC_RX_SEPARETED_MAIN,
.analog_in_ports = 2,
.analog_out_ports = 2,
};
#define SND_MOTU_DEV_ENTRY(model, data) \
{ \
.match_flags = IEEE1394_MATCH_VENDOR_ID | \
@@ -265,6 +276,7 @@ static const struct ieee1394_device_id motu_id_table[] = {
SND_MOTU_DEV_ENTRY(0x000015, &motu_828mk3), /* FireWire only. */
SND_MOTU_DEV_ENTRY(0x000035, &motu_828mk3), /* Hybrid. */
SND_MOTU_DEV_ENTRY(0x000033, &motu_audio_express),
SND_MOTU_DEV_ENTRY(0x000045, &motu_4pre),
{ }
};
MODULE_DEVICE_TABLE(ieee1394, motu_id_table);

View File

@@ -69,6 +69,8 @@ struct snd_motu {
int dev_lock_count;
bool dev_lock_changed;
wait_queue_head_t hwdep_wait;
struct amdtp_domain domain;
};
enum snd_motu_spec_flags {

View File

@@ -114,19 +114,13 @@ static int start_stream(struct snd_oxfw *oxfw, struct amdtp_stream *stream)
if (err < 0)
return err;
err = amdtp_stream_start(stream, conn->resources.channel, conn->speed);
err = amdtp_domain_add_stream(&oxfw->domain, stream,
conn->resources.channel, conn->speed);
if (err < 0) {
cmp_connection_break(conn);
return err;
}
// Wait first packet.
if (!amdtp_stream_wait_callback(stream, CALLBACK_TIMEOUT)) {
amdtp_stream_stop(stream);
cmp_connection_break(conn);
return -ETIMEDOUT;
}
return 0;
}
@@ -280,12 +274,12 @@ int snd_oxfw_stream_reserve_duplex(struct snd_oxfw *oxfw,
pcm_channels = formation.pcm;
}
if (formation.rate != rate || formation.pcm != pcm_channels) {
amdtp_stream_stop(&oxfw->rx_stream);
amdtp_domain_stop(&oxfw->domain);
cmp_connection_break(&oxfw->in_conn);
cmp_connection_release(&oxfw->in_conn);
if (oxfw->has_output) {
amdtp_stream_stop(&oxfw->tx_stream);
cmp_connection_break(&oxfw->out_conn);
cmp_connection_release(&oxfw->out_conn);
}
@@ -325,30 +319,46 @@ int snd_oxfw_stream_start_duplex(struct snd_oxfw *oxfw)
if (amdtp_streaming_error(&oxfw->rx_stream) ||
amdtp_streaming_error(&oxfw->tx_stream)) {
amdtp_stream_stop(&oxfw->rx_stream);
cmp_connection_break(&oxfw->in_conn);
amdtp_domain_stop(&oxfw->domain);
if (oxfw->has_output) {
amdtp_stream_stop(&oxfw->tx_stream);
cmp_connection_break(&oxfw->in_conn);
if (oxfw->has_output)
cmp_connection_break(&oxfw->out_conn);
}
}
if (!amdtp_stream_running(&oxfw->rx_stream)) {
err = start_stream(oxfw, &oxfw->rx_stream);
if (err < 0) {
dev_err(&oxfw->unit->device,
"fail to start rx stream: %d\n", err);
"fail to prepare rx stream: %d\n", err);
goto error;
}
}
if (oxfw->has_output) {
if (!amdtp_stream_running(&oxfw->tx_stream)) {
if (oxfw->has_output &&
!amdtp_stream_running(&oxfw->tx_stream)) {
err = start_stream(oxfw, &oxfw->tx_stream);
if (err < 0) {
dev_err(&oxfw->unit->device,
"fail to start tx stream: %d\n", err);
"fail to prepare tx stream: %d\n", err);
goto error;
}
}
err = amdtp_domain_start(&oxfw->domain);
if (err < 0)
goto error;
// Wait first packet.
if (!amdtp_stream_wait_callback(&oxfw->rx_stream,
CALLBACK_TIMEOUT)) {
err = -ETIMEDOUT;
goto error;
}
if (oxfw->has_output) {
if (!amdtp_stream_wait_callback(&oxfw->tx_stream,
CALLBACK_TIMEOUT)) {
err = -ETIMEDOUT;
goto error;
}
}
@@ -356,24 +366,24 @@ int snd_oxfw_stream_start_duplex(struct snd_oxfw *oxfw)
return 0;
error:
amdtp_stream_stop(&oxfw->rx_stream);
amdtp_domain_stop(&oxfw->domain);
cmp_connection_break(&oxfw->in_conn);
if (oxfw->has_output) {
amdtp_stream_stop(&oxfw->tx_stream);
if (oxfw->has_output)
cmp_connection_break(&oxfw->out_conn);
}
return err;
}
void snd_oxfw_stream_stop_duplex(struct snd_oxfw *oxfw)
{
if (oxfw->substreams_count == 0) {
amdtp_stream_stop(&oxfw->rx_stream);
amdtp_domain_stop(&oxfw->domain);
cmp_connection_break(&oxfw->in_conn);
cmp_connection_release(&oxfw->in_conn);
if (oxfw->has_output) {
amdtp_stream_stop(&oxfw->tx_stream);
cmp_connection_break(&oxfw->out_conn);
cmp_connection_release(&oxfw->out_conn);
}
@@ -409,13 +419,22 @@ int snd_oxfw_stream_init_duplex(struct snd_oxfw *oxfw)
}
}
return 0;
err = amdtp_domain_init(&oxfw->domain);
if (err < 0) {
destroy_stream(oxfw, &oxfw->rx_stream);
if (oxfw->has_output)
destroy_stream(oxfw, &oxfw->tx_stream);
}
return err;
}
// This function should be called before starting the stream or after stopping
// the streams.
void snd_oxfw_stream_destroy_duplex(struct snd_oxfw *oxfw)
{
amdtp_domain_destroy(&oxfw->domain);
destroy_stream(oxfw, &oxfw->rx_stream);
if (oxfw->has_output)
@@ -424,13 +443,13 @@ void snd_oxfw_stream_destroy_duplex(struct snd_oxfw *oxfw)
void snd_oxfw_stream_update_duplex(struct snd_oxfw *oxfw)
{
amdtp_stream_stop(&oxfw->rx_stream);
amdtp_domain_stop(&oxfw->domain);
cmp_connection_break(&oxfw->in_conn);
amdtp_stream_pcm_abort(&oxfw->rx_stream);
if (oxfw->has_output) {
amdtp_stream_stop(&oxfw->tx_stream);
cmp_connection_break(&oxfw->out_conn);
amdtp_stream_pcm_abort(&oxfw->tx_stream);

View File

@@ -63,6 +63,8 @@ struct snd_oxfw {
const struct ieee1394_device_id *entry;
void *spec;
struct amdtp_domain domain;
};
/*

View File

@@ -32,19 +32,24 @@ int amdtp_tscm_set_parameters(struct amdtp_stream *s, unsigned int rate)
return amdtp_stream_set_parameters(s, rate, data_channels);
}
static void write_pcm_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,
unsigned int pcm_frames)
{
struct amdtp_tscm *p = s->protocol;
unsigned int channels = p->pcm_channels;
struct snd_pcm_runtime *runtime = pcm->runtime;
unsigned int channels, remaining_frames, i, c;
unsigned int pcm_buffer_pointer;
int remaining_frames;
const u32 *src;
int i, c;
pcm_buffer_pointer = s->pcm_buffer_pointer + pcm_frames;
pcm_buffer_pointer %= runtime->buffer_size;
channels = p->pcm_channels;
src = (void *)runtime->dma_area +
frames_to_bytes(runtime, s->pcm_buffer_pointer);
remaining_frames = runtime->buffer_size - s->pcm_buffer_pointer;
frames_to_bytes(runtime, pcm_buffer_pointer);
remaining_frames = runtime->buffer_size - pcm_buffer_pointer;
for (i = 0; i < frames; ++i) {
for (c = 0; c < channels; ++c) {
@@ -57,19 +62,24 @@ static void write_pcm_s32(struct amdtp_stream *s,
}
}
static void read_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,
unsigned int pcm_frames)
{
struct amdtp_tscm *p = s->protocol;
unsigned int channels = p->pcm_channels;
struct snd_pcm_runtime *runtime = pcm->runtime;
unsigned int channels, remaining_frames, i, c;
unsigned int pcm_buffer_pointer;
int remaining_frames;
u32 *dst;
int i, c;
pcm_buffer_pointer = s->pcm_buffer_pointer + pcm_frames;
pcm_buffer_pointer %= runtime->buffer_size;
channels = p->pcm_channels;
dst = (void *)runtime->dma_area +
frames_to_bytes(runtime, s->pcm_buffer_pointer);
remaining_frames = runtime->buffer_size - s->pcm_buffer_pointer;
frames_to_bytes(runtime, pcm_buffer_pointer);
remaining_frames = runtime->buffer_size - pcm_buffer_pointer;
/* The first data channel is for event counter. */
buffer += 1;
@@ -165,65 +175,82 @@ static void read_status_messages(struct amdtp_stream *s,
}
}
static unsigned int process_tx_data_blocks(struct amdtp_stream *s,
__be32 *buffer,
unsigned int data_blocks,
unsigned int *syt)
static unsigned int process_ir_ctx_payloads(struct amdtp_stream *s,
const struct pkt_desc *descs,
unsigned int packets,
struct snd_pcm_substream *pcm)
{
struct snd_pcm_substream *pcm;
unsigned int pcm_frames = 0;
int i;
pcm = READ_ONCE(s->pcm);
if (data_blocks > 0 && pcm)
read_pcm_s32(s, pcm, buffer, data_blocks);
for (i = 0; i < packets; ++i) {
const struct pkt_desc *desc = descs + i;
__be32 *buf = desc->ctx_payload;
unsigned int data_blocks = desc->data_blocks;
read_status_messages(s, buffer, data_blocks);
if (pcm) {
read_pcm_s32(s, pcm, buf, data_blocks, pcm_frames);
pcm_frames += data_blocks;
}
return data_blocks;
read_status_messages(s, buf, data_blocks);
}
return pcm_frames;
}
static unsigned int process_rx_data_blocks(struct amdtp_stream *s,
__be32 *buffer,
unsigned int data_blocks,
unsigned int *syt)
static unsigned int process_it_ctx_payloads(struct amdtp_stream *s,
const struct pkt_desc *descs,
unsigned int packets,
struct snd_pcm_substream *pcm)
{
struct snd_pcm_substream *pcm;
unsigned int pcm_frames = 0;
int i;
/* This field is not used. */
*syt = 0x0000;
for (i = 0; i < packets; ++i) {
const struct pkt_desc *desc = descs + i;
__be32 *buf = desc->ctx_payload;
unsigned int data_blocks = desc->data_blocks;
pcm = READ_ONCE(s->pcm);
if (pcm)
write_pcm_s32(s, pcm, buffer, data_blocks);
else
write_pcm_silence(s, buffer, data_blocks);
if (pcm) {
write_pcm_s32(s, pcm, buf, data_blocks, pcm_frames);
pcm_frames += data_blocks;
} else {
write_pcm_silence(s, buf, data_blocks);
}
}
return data_blocks;
return pcm_frames;
}
int amdtp_tscm_init(struct amdtp_stream *s, struct fw_unit *unit,
enum amdtp_stream_direction dir, unsigned int pcm_channels)
{
amdtp_stream_process_data_blocks_t process_data_blocks;
amdtp_stream_process_ctx_payloads_t process_ctx_payloads;
struct amdtp_tscm *p;
unsigned int fmt;
int err;
if (dir == AMDTP_IN_STREAM) {
fmt = AMDTP_FMT_TSCM_TX;
process_data_blocks = process_tx_data_blocks;
process_ctx_payloads = process_ir_ctx_payloads;
} else {
fmt = AMDTP_FMT_TSCM_RX;
process_data_blocks = process_rx_data_blocks;
process_ctx_payloads = process_it_ctx_payloads;
}
err = amdtp_stream_init(s, unit, dir,
CIP_NONBLOCKING | CIP_SKIP_DBC_ZERO_CHECK, fmt,
process_data_blocks, sizeof(struct amdtp_tscm));
CIP_NONBLOCKING | CIP_SKIP_DBC_ZERO_CHECK, fmt,
process_ctx_payloads, sizeof(struct amdtp_tscm));
if (err < 0)
return 0;
/* Use fixed value for FDF field. */
s->ctx_data.rx.fdf = 0x00;
if (dir == AMDTP_OUT_STREAM) {
// Use fixed value for FDF field.
s->ctx_data.rx.fdf = 0x00;
// Not used.
s->ctx_data.rx.syt_override = 0x0000;
}
/* This protocol uses fixed number of data channels for PCM samples. */
p = s->protocol;

View File

@@ -194,9 +194,6 @@ static void finish_session(struct snd_tscm *tscm)
{
__be32 reg;
amdtp_stream_stop(&tscm->rx_stream);
amdtp_stream_stop(&tscm->tx_stream);
reg = 0;
snd_fw_transaction(tscm->unit, TCODE_WRITE_QUADLET_REQUEST,
TSCM_ADDR_BASE + TSCM_OFFSET_START_STREAMING,
@@ -301,38 +298,68 @@ static int keep_resources(struct snd_tscm *tscm, unsigned int rate,
fw_parent_device(tscm->unit)->max_speed);
}
int snd_tscm_stream_init_duplex(struct snd_tscm *tscm)
static int init_stream(struct snd_tscm *tscm, struct amdtp_stream *s)
{
struct fw_iso_resources *resources;
enum amdtp_stream_direction dir;
unsigned int pcm_channels;
int err;
/* For out-stream. */
err = fw_iso_resources_init(&tscm->rx_resources, tscm->unit);
if (err < 0)
return err;
pcm_channels = tscm->spec->pcm_playback_analog_channels;
if (s == &tscm->tx_stream) {
resources = &tscm->tx_resources;
dir = AMDTP_IN_STREAM;
pcm_channels = tscm->spec->pcm_capture_analog_channels;
} else {
resources = &tscm->rx_resources;
dir = AMDTP_OUT_STREAM;
pcm_channels = tscm->spec->pcm_playback_analog_channels;
}
if (tscm->spec->has_adat)
pcm_channels += 8;
if (tscm->spec->has_spdif)
pcm_channels += 2;
err = amdtp_tscm_init(&tscm->rx_stream, tscm->unit, AMDTP_OUT_STREAM,
pcm_channels);
err = fw_iso_resources_init(resources, tscm->unit);
if (err < 0)
return err;
/* For in-stream. */
err = fw_iso_resources_init(&tscm->tx_resources, tscm->unit);
err = amdtp_tscm_init(s, tscm->unit, dir, pcm_channels);
if (err < 0)
fw_iso_resources_free(resources);
return err;
}
static void destroy_stream(struct snd_tscm *tscm, struct amdtp_stream *s)
{
amdtp_stream_destroy(s);
if (s == &tscm->tx_stream)
fw_iso_resources_destroy(&tscm->tx_resources);
else
fw_iso_resources_destroy(&tscm->rx_resources);
}
int snd_tscm_stream_init_duplex(struct snd_tscm *tscm)
{
int err;
err = init_stream(tscm, &tscm->tx_stream);
if (err < 0)
return err;
pcm_channels = tscm->spec->pcm_capture_analog_channels;
if (tscm->spec->has_adat)
pcm_channels += 8;
if (tscm->spec->has_spdif)
pcm_channels += 2;
err = amdtp_tscm_init(&tscm->tx_stream, tscm->unit, AMDTP_IN_STREAM,
pcm_channels);
if (err < 0)
amdtp_stream_destroy(&tscm->rx_stream);
err = init_stream(tscm, &tscm->rx_stream);
if (err < 0) {
destroy_stream(tscm, &tscm->tx_stream);
return err;
}
err = amdtp_domain_init(&tscm->domain);
if (err < 0) {
destroy_stream(tscm, &tscm->tx_stream);
destroy_stream(tscm, &tscm->rx_stream);
}
return err;
}
@@ -340,24 +367,20 @@ int snd_tscm_stream_init_duplex(struct snd_tscm *tscm)
// At bus reset, streaming is stopped and some registers are clear.
void snd_tscm_stream_update_duplex(struct snd_tscm *tscm)
{
amdtp_stream_pcm_abort(&tscm->tx_stream);
amdtp_stream_stop(&tscm->tx_stream);
amdtp_domain_stop(&tscm->domain);
amdtp_stream_pcm_abort(&tscm->tx_stream);
amdtp_stream_pcm_abort(&tscm->rx_stream);
amdtp_stream_stop(&tscm->rx_stream);
}
/*
* This function should be called before starting streams or after stopping
* streams.
*/
// This function should be called before starting streams or after stopping
// streams.
void snd_tscm_stream_destroy_duplex(struct snd_tscm *tscm)
{
amdtp_stream_destroy(&tscm->rx_stream);
amdtp_stream_destroy(&tscm->tx_stream);
amdtp_domain_destroy(&tscm->domain);
fw_iso_resources_destroy(&tscm->rx_resources);
fw_iso_resources_destroy(&tscm->tx_resources);
destroy_stream(tscm, &tscm->rx_stream);
destroy_stream(tscm, &tscm->tx_stream);
}
int snd_tscm_stream_reserve_duplex(struct snd_tscm *tscm, unsigned int rate)
@@ -370,6 +393,8 @@ int snd_tscm_stream_reserve_duplex(struct snd_tscm *tscm, unsigned int rate)
return err;
if (tscm->substreams_counter == 0 || rate != curr_rate) {
amdtp_domain_stop(&tscm->domain);
finish_session(tscm);
fw_iso_resources_free(&tscm->tx_resources);
@@ -402,8 +427,10 @@ int snd_tscm_stream_start_duplex(struct snd_tscm *tscm, unsigned int rate)
return 0;
if (amdtp_streaming_error(&tscm->rx_stream) ||
amdtp_streaming_error(&tscm->tx_stream))
amdtp_streaming_error(&tscm->tx_stream)) {
amdtp_domain_stop(&tscm->domain);
finish_session(tscm);
}
if (generation != fw_parent_device(tscm->unit)->card->generation) {
err = fw_iso_resources_update(&tscm->tx_resources);
@@ -416,6 +443,8 @@ int snd_tscm_stream_start_duplex(struct snd_tscm *tscm, unsigned int rate)
}
if (!amdtp_stream_running(&tscm->rx_stream)) {
int spd = fw_parent_device(tscm->unit)->max_speed;
err = set_stream_formats(tscm, rate);
if (err < 0)
goto error;
@@ -424,27 +453,23 @@ int snd_tscm_stream_start_duplex(struct snd_tscm *tscm, unsigned int rate)
if (err < 0)
goto error;
err = amdtp_stream_start(&tscm->rx_stream,
tscm->rx_resources.channel,
fw_parent_device(tscm->unit)->max_speed);
err = amdtp_domain_add_stream(&tscm->domain, &tscm->rx_stream,
tscm->rx_resources.channel, spd);
if (err < 0)
goto error;
err = amdtp_domain_add_stream(&tscm->domain, &tscm->tx_stream,
tscm->tx_resources.channel, spd);
if (err < 0)
goto error;
err = amdtp_domain_start(&tscm->domain);
if (err < 0)
return err;
if (!amdtp_stream_wait_callback(&tscm->rx_stream,
CALLBACK_TIMEOUT)) {
err = -ETIMEDOUT;
goto error;
}
}
if (!amdtp_stream_running(&tscm->tx_stream)) {
err = amdtp_stream_start(&tscm->tx_stream,
tscm->tx_resources.channel,
fw_parent_device(tscm->unit)->max_speed);
if (err < 0)
goto error;
if (!amdtp_stream_wait_callback(&tscm->tx_stream,
CALLBACK_TIMEOUT) ||
!amdtp_stream_wait_callback(&tscm->tx_stream,
CALLBACK_TIMEOUT)) {
err = -ETIMEDOUT;
goto error;
@@ -453,6 +478,7 @@ int snd_tscm_stream_start_duplex(struct snd_tscm *tscm, unsigned int rate)
return 0;
error:
amdtp_domain_stop(&tscm->domain);
finish_session(tscm);
return err;
@@ -461,6 +487,7 @@ error:
void snd_tscm_stream_stop_duplex(struct snd_tscm *tscm)
{
if (tscm->substreams_counter == 0) {
amdtp_domain_stop(&tscm->domain);
finish_session(tscm);
fw_iso_resources_free(&tscm->tx_resources);

View File

@@ -39,6 +39,9 @@ static const struct snd_tscm_spec model_specs[] = {
.midi_capture_ports = 2,
.midi_playback_ports = 4,
},
// This kernel module doesn't support FE-8 because the most of features
// can be implemented in userspace without any specific support of this
// module.
};
static int identify_model(struct snd_tscm *tscm)
@@ -214,7 +217,6 @@ static const struct ieee1394_device_id snd_tscm_id_table[] = {
.vendor_id = 0x00022e,
.specifier_id = 0x00022e,
},
/* FE-08 requires reverse-engineering because it just has faders. */
{}
};
MODULE_DEVICE_TABLE(ieee1394, snd_tscm_id_table);

View File

@@ -97,6 +97,8 @@ struct snd_tscm {
struct snd_firewire_tascam_change queue[SND_TSCM_QUEUE_COUNT];
unsigned int pull_pos;
unsigned int push_pos;
struct amdtp_domain domain;
};
#define TSCM_ADDR_BASE 0xffff00000000ull
@@ -127,6 +129,26 @@ struct snd_tscm {
#define TSCM_OFFSET_MIDI_RX_QUAD 0x4000
// Although FE-8 supports the above registers, it has no I/O interfaces for
// audio samples and music messages. Otherwise it supports another notification
// for status and control message as well as LED brightening. The message
// consists of quadlet-aligned data up to 32 quadlets. The first byte of message
// is fixed to 0x40. The second byte is between 0x00 to 0x1f and represent each
// control:
// fader: 0x00-0x07
// button: 0x0d, 0x0e
// knob: 0x14-0x1b
// sensing: 0x0b
//
// The rest two bytes represent state of the controls; e.g. current value for
// fader and knob, bitmasks for button and sensing.
// Just after turning on, 32 quadlets messages with 0x00-0x1f are immediately
// sent in one transaction. After, several quadlets are sent in one transaction.
//
// TSCM_OFFSET_FE8_CTL_TX_ON 0x0310
// TSCM_OFFSET_FE8_CTL_TX_ADDR_HI 0x0314
// TSCM_OFFSET_FE8_CTL_TX_ADDR_LO 0x0318
enum snd_tscm_clock {
SND_TSCM_CLOCK_INTERNAL = 0,
SND_TSCM_CLOCK_WORD = 1,