Merge branch 'for-next' into for-linus
Signed-off-by: Takashi Iwai <tiwai@suse.de>
This commit is contained in:
@@ -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);
|
||||
|
@@ -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;
|
||||
|
@@ -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, ¶ms) < 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, ¶ms) < 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, ¶ms);
|
||||
}
|
||||
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);
|
||||
|
@@ -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
|
||||
|
@@ -115,6 +115,8 @@ struct snd_bebob {
|
||||
|
||||
/* For BeBoB version quirk. */
|
||||
unsigned int version;
|
||||
|
||||
struct amdtp_domain domain;
|
||||
};
|
||||
|
||||
static inline int
|
||||
|
@@ -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);
|
||||
}
|
||||
|
@@ -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,
|
||||
®, sizeof(reg));
|
||||
} else {
|
||||
amdtp_stream_stop(&dice->rx_stream[i]);
|
||||
|
||||
snd_dice_transaction_write_rx(dice,
|
||||
params->size * i + RX_ISOCHRONOUS,
|
||||
®, 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);
|
||||
}
|
||||
|
@@ -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 {
|
||||
|
@@ -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)
|
||||
|
@@ -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);
|
||||
|
@@ -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
|
||||
|
@@ -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));
|
||||
}
|
||||
|
@@ -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)
|
||||
|
@@ -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 {
|
||||
|
@@ -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,
|
||||
|
@@ -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);
|
||||
}
|
||||
|
@@ -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;
|
||||
}
|
||||
|
@@ -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, ®,
|
||||
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;
|
||||
}
|
||||
|
@@ -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);
|
||||
|
@@ -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 {
|
||||
|
@@ -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);
|
||||
|
@@ -63,6 +63,8 @@ struct snd_oxfw {
|
||||
|
||||
const struct ieee1394_device_id *entry;
|
||||
void *spec;
|
||||
|
||||
struct amdtp_domain domain;
|
||||
};
|
||||
|
||||
/*
|
||||
|
@@ -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;
|
||||
|
@@ -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);
|
||||
|
@@ -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);
|
||||
|
@@ -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,
|
||||
|
Reference in New Issue
Block a user