Skip to content

Commit

Permalink
ALSA: firewire-lib: Add support for channel mapping
Browse files Browse the repository at this point in the history
Some devices arrange the position of PCM/MIDI data channel in AMDTP packet.
This commit allows drivers to set channel mapping.

To be simple, the mapping table is an array with fixed length. Then the number
of channels for PCM is restricted by 64 channels.

Signed-off-by: Takashi Sakamoto <o-takashi@sakamocchi.jp>
Signed-off-by: Takashi Iwai <tiwai@suse.de>
  • Loading branch information
Takashi Sakamoto authored and Takashi Iwai committed May 26, 2014
1 parent 7b3b0d8 commit 77d2a8a
Show file tree
Hide file tree
Showing 2 changed files with 79 additions and 47 deletions.
118 changes: 71 additions & 47 deletions sound/firewire/amdtp.c
Original file line number Diff line number Diff line change
Expand Up @@ -129,11 +129,12 @@ void amdtp_stream_set_parameters(struct amdtp_stream *s,
[CIP_SFC_176400] = 176400,
[CIP_SFC_192000] = 192000,
};
unsigned int sfc, midi_channels;
unsigned int i, sfc, midi_channels;

midi_channels = DIV_ROUND_UP(midi_ports, 8);

if (WARN_ON(amdtp_stream_running(s)) ||
if (WARN_ON(amdtp_stream_running(s)) |
WARN_ON(pcm_channels > AMDTP_MAX_CHANNELS_FOR_PCM) |
WARN_ON(midi_channels > AMDTP_MAX_CHANNELS_FOR_MIDI))
return;

Expand All @@ -148,11 +149,12 @@ void amdtp_stream_set_parameters(struct amdtp_stream *s,
if (s->dual_wire) {
sfc -= 2;
rate /= 2;
pcm_channels *= 2;
s->pcm_channels = pcm_channels * 2;
} else {
s->pcm_channels = pcm_channels;
}
s->sfc = sfc;
s->data_block_quadlets = pcm_channels + midi_channels;
s->pcm_channels = pcm_channels;
s->data_block_quadlets = s->pcm_channels + midi_channels;
s->midi_ports = midi_ports;

s->syt_interval = amdtp_syt_intervals[sfc];
Expand All @@ -162,6 +164,11 @@ void amdtp_stream_set_parameters(struct amdtp_stream *s,
if (s->flags & CIP_BLOCKING)
/* additional buffering needed to adjust for no-data packets */
s->transfer_delay += TICKS_PER_SECOND * s->syt_interval / rate;

/* init the position map for PCM and MIDI channels */
for (i = 0; i < pcm_channels; i++)
s->pcm_positions[i] = i;
s->midi_position = s->pcm_channels;
}
EXPORT_SYMBOL(amdtp_stream_set_parameters);

Expand Down Expand Up @@ -341,22 +348,21 @@ static void amdtp_write_s32(struct amdtp_stream *s,
__be32 *buffer, unsigned int frames)
{
struct snd_pcm_runtime *runtime = pcm->runtime;
unsigned int channels, remaining_frames, frame_step, i, c;
unsigned int channels, remaining_frames, i, c;
const u32 *src;

channels = s->pcm_channels;
src = (void *)runtime->dma_area +
frames_to_bytes(runtime, s->pcm_buffer_pointer);
remaining_frames = runtime->buffer_size - s->pcm_buffer_pointer;
frame_step = s->data_block_quadlets - channels;

for (i = 0; i < frames; ++i) {
for (c = 0; c < channels; ++c) {
*buffer = cpu_to_be32((*src >> 8) | 0x40000000);
buffer[s->pcm_positions[c]] =
cpu_to_be32((*src >> 8) | 0x40000000);
src++;
buffer++;
}
buffer += frame_step;
buffer += s->data_block_quadlets;
if (--remaining_frames == 0)
src = (void *)runtime->dma_area;
}
Expand All @@ -367,22 +373,21 @@ static void amdtp_write_s16(struct amdtp_stream *s,
__be32 *buffer, unsigned int frames)
{
struct snd_pcm_runtime *runtime = pcm->runtime;
unsigned int channels, remaining_frames, frame_step, i, c;
unsigned int channels, remaining_frames, i, c;
const u16 *src;

channels = s->pcm_channels;
src = (void *)runtime->dma_area +
frames_to_bytes(runtime, s->pcm_buffer_pointer);
remaining_frames = runtime->buffer_size - s->pcm_buffer_pointer;
frame_step = s->data_block_quadlets - channels;

for (i = 0; i < frames; ++i) {
for (c = 0; c < channels; ++c) {
*buffer = cpu_to_be32((*src << 8) | 0x40000000);
buffer[s->pcm_positions[c]] =
cpu_to_be32((*src << 8) | 0x40000000);
src++;
buffer++;
}
buffer += frame_step;
buffer += s->data_block_quadlets;
if (--remaining_frames == 0)
src = (void *)runtime->dma_area;
}
Expand All @@ -393,29 +398,29 @@ static void amdtp_write_s32_dualwire(struct amdtp_stream *s,
__be32 *buffer, unsigned int frames)
{
struct snd_pcm_runtime *runtime = pcm->runtime;
unsigned int channels, frame_adjust_1, frame_adjust_2, i, c;
unsigned int channels, remaining_frames, i, c;
const u32 *src;

channels = s->pcm_channels;
src = (void *)runtime->dma_area +
s->pcm_buffer_pointer * (runtime->frame_bits / 8);
frame_adjust_1 = channels - 1;
frame_adjust_2 = 1 - (s->data_block_quadlets - channels);
frames_to_bytes(runtime, s->pcm_buffer_pointer);
remaining_frames = runtime->buffer_size - s->pcm_buffer_pointer;
channels = s->pcm_channels / 2;

channels /= 2;
for (i = 0; i < frames; ++i) {
for (c = 0; c < channels; ++c) {
*buffer = cpu_to_be32((*src >> 8) | 0x40000000);
buffer[s->pcm_positions[c] * 2] =
cpu_to_be32((*src >> 8) | 0x40000000);
src++;
buffer += 2;
}
buffer -= frame_adjust_1;
buffer += 1;
for (c = 0; c < channels; ++c) {
*buffer = cpu_to_be32((*src >> 8) | 0x40000000);
buffer[s->pcm_positions[c] * 2] =
cpu_to_be32((*src >> 8) | 0x40000000);
src++;
buffer += 2;
}
buffer -= frame_adjust_2;
buffer += s->data_block_quadlets - 1;
if (--remaining_frames == 0)
src = (void *)runtime->dma_area;
}
}

Expand All @@ -424,29 +429,29 @@ static void amdtp_write_s16_dualwire(struct amdtp_stream *s,
__be32 *buffer, unsigned int frames)
{
struct snd_pcm_runtime *runtime = pcm->runtime;
unsigned int channels, frame_adjust_1, frame_adjust_2, i, c;
unsigned int channels, remaining_frames, i, c;
const u16 *src;

channels = s->pcm_channels;
src = (void *)runtime->dma_area +
s->pcm_buffer_pointer * (runtime->frame_bits / 8);
frame_adjust_1 = channels - 1;
frame_adjust_2 = 1 - (s->data_block_quadlets - channels);
frames_to_bytes(runtime, s->pcm_buffer_pointer);
remaining_frames = runtime->buffer_size - s->pcm_buffer_pointer;
channels = s->pcm_channels / 2;

channels /= 2;
for (i = 0; i < frames; ++i) {
for (c = 0; c < channels; ++c) {
*buffer = cpu_to_be32((*src << 8) | 0x40000000);
buffer[s->pcm_positions[c] * 2] =
cpu_to_be32((*src << 8) | 0x40000000);
src++;
buffer += 2;
}
buffer -= frame_adjust_1;
buffer += 1;
for (c = 0; c < channels; ++c) {
*buffer = cpu_to_be32((*src << 8) | 0x40000000);
buffer[s->pcm_positions[c] * 2] =
cpu_to_be32((*src << 8) | 0x40000000);
src++;
buffer += 2;
}
buffer -= frame_adjust_2;
buffer += s->data_block_quadlets - 1;
if (--remaining_frames == 0)
src = (void *)runtime->dma_area;
}
}

Expand All @@ -465,7 +470,7 @@ static void amdtp_read_s32(struct amdtp_stream *s,

for (i = 0; i < frames; ++i) {
for (c = 0; c < channels; ++c) {
*dst = be32_to_cpu(buffer[c]) << 8;
*dst = be32_to_cpu(buffer[s->pcm_positions[c]]) << 8;
dst++;
}
buffer += s->data_block_quadlets;
Expand All @@ -489,12 +494,14 @@ static void amdtp_read_s32_dualwire(struct amdtp_stream *s,

for (i = 0; i < frames; ++i) {
for (c = 0; c < channels; ++c) {
*dst = be32_to_cpu(buffer[c * 2]) << 8;
*dst =
be32_to_cpu(buffer[s->pcm_positions[c] * 2]) << 8;
dst++;
}
buffer += 1;
for (c = 0; c < channels; ++c) {
*dst = be32_to_cpu(buffer[c * 2]) << 8;
*dst =
be32_to_cpu(buffer[s->pcm_positions[c] * 2]) << 8;
dst++;
}
buffer += s->data_block_quadlets - 1;
Expand All @@ -510,20 +517,35 @@ static void amdtp_fill_pcm_silence(struct amdtp_stream *s,

for (i = 0; i < frames; ++i) {
for (c = 0; c < s->pcm_channels; ++c)
buffer[c] = cpu_to_be32(0x40000000);
buffer[s->pcm_positions[c]] = cpu_to_be32(0x40000000);
buffer += s->data_block_quadlets;
}
}

static void amdtp_fill_pcm_silence_dualwire(struct amdtp_stream *s,
__be32 *buffer, unsigned int frames)
{
unsigned int i, c, channels;

channels = s->pcm_channels / 2;
for (i = 0; i < frames; ++i) {
for (c = 0; c < channels; ++c) {
buffer[s->pcm_positions[c] * 2] =
buffer[s->pcm_positions[c] * 2 + 1] =
cpu_to_be32(0x40000000);
}
buffer += s->data_block_quadlets;
}
}
static void amdtp_fill_midi(struct amdtp_stream *s,
__be32 *buffer, unsigned int frames)
{
unsigned int f, port;
u8 *b;

for (f = 0; f < frames; f++) {
buffer[s->pcm_channels + 1] = 0;
b = (u8 *)&buffer[s->pcm_channels + 1];
buffer[s->midi_position] = 0;
b = (u8 *)&buffer[s->midi_position];

port = (s->data_block_counter + f) % 8;
if ((s->midi[port] == NULL) ||
Expand All @@ -545,7 +567,7 @@ static void amdtp_pull_midi(struct amdtp_stream *s,

for (f = 0; f < frames; f++) {
port = (s->data_block_counter + f) % 8;
b = (u8 *)&buffer[s->pcm_channels + 1];
b = (u8 *)&buffer[s->midi_position];

len = b[0] - 0x80;
if ((1 <= len) && (len <= 3) && (s->midi[port]))
Expand Down Expand Up @@ -652,6 +674,8 @@ static void handle_out_packet(struct amdtp_stream *s, unsigned int syt)
pcm = ACCESS_ONCE(s->pcm);
if (pcm)
s->transfer_samples(s, pcm, buffer, data_blocks);
else if (s->dual_wire)
amdtp_fill_pcm_silence_dualwire(s, buffer, data_blocks);
else
amdtp_fill_pcm_silence(s, buffer, data_blocks);
if (s->midi_ports)
Expand Down Expand Up @@ -683,7 +707,7 @@ static void handle_in_packet(struct amdtp_stream *s,

/*
* This module supports 'Two-quadlet CIP header with SYT field'.
* For convinience, also check FMT field is AM824 or not.
* For convenience, also check FMT field is AM824 or not.
*/
if (((cip_header[0] & CIP_EOH_MASK) == CIP_EOH) ||
((cip_header[1] & CIP_EOH_MASK) != CIP_EOH) ||
Expand Down
8 changes: 8 additions & 0 deletions sound/firewire/amdtp.h
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,12 @@ enum cip_sfc {
SNDRV_PCM_FMTBIT_S32)


/*
* This module supports maximum 64 PCM channels for one PCM stream
* This is for our convenience.
*/
#define AMDTP_MAX_CHANNELS_FOR_PCM 64

/*
* AMDTP packet can include channels for MIDI conformant data.
* Each MIDI conformant data channel includes 8 MPX-MIDI data stream.
Expand Down Expand Up @@ -85,6 +91,8 @@ struct amdtp_stream {
void (*transfer_samples)(struct amdtp_stream *s,
struct snd_pcm_substream *pcm,
__be32 *buffer, unsigned int frames);
u8 pcm_positions[AMDTP_MAX_CHANNELS_FOR_PCM];
u8 midi_position;

unsigned int syt_interval;
unsigned int transfer_delay;
Expand Down

0 comments on commit 77d2a8a

Please sign in to comment.