|
@@ -65,42 +65,66 @@ void amdtp_out_stream_destroy(struct amdtp_out_stream *s)
|
|
|
}
|
|
|
EXPORT_SYMBOL(amdtp_out_stream_destroy);
|
|
|
|
|
|
+unsigned int amdtp_syt_intervals[CIP_SFC_COUNT] = {
|
|
|
+ [CIP_SFC_32000] = 8,
|
|
|
+ [CIP_SFC_44100] = 8,
|
|
|
+ [CIP_SFC_48000] = 8,
|
|
|
+ [CIP_SFC_88200] = 16,
|
|
|
+ [CIP_SFC_96000] = 16,
|
|
|
+ [CIP_SFC_176400] = 32,
|
|
|
+ [CIP_SFC_192000] = 32,
|
|
|
+};
|
|
|
+EXPORT_SYMBOL(amdtp_syt_intervals);
|
|
|
+
|
|
|
/**
|
|
|
- * amdtp_out_stream_set_rate - set the sample rate
|
|
|
+ * amdtp_out_stream_set_parameters - set stream parameters
|
|
|
* @s: the AMDTP output stream to configure
|
|
|
* @rate: the sample rate
|
|
|
+ * @pcm_channels: the number of PCM samples in each data block, to be encoded
|
|
|
+ * as AM824 multi-bit linear audio
|
|
|
+ * @midi_ports: the number of MIDI ports (i.e., MPX-MIDI Data Channels)
|
|
|
*
|
|
|
- * The sample rate must be set before the stream is started, and must not be
|
|
|
+ * The parameters must be set before the stream is started, and must not be
|
|
|
* changed while the stream is running.
|
|
|
*/
|
|
|
-void amdtp_out_stream_set_rate(struct amdtp_out_stream *s, unsigned int rate)
|
|
|
+void amdtp_out_stream_set_parameters(struct amdtp_out_stream *s,
|
|
|
+ unsigned int rate,
|
|
|
+ unsigned int pcm_channels,
|
|
|
+ unsigned int midi_ports)
|
|
|
{
|
|
|
- static const struct {
|
|
|
- unsigned int rate;
|
|
|
- unsigned int syt_interval;
|
|
|
- } rate_info[] = {
|
|
|
- [CIP_SFC_32000] = { 32000, 8, },
|
|
|
- [CIP_SFC_44100] = { 44100, 8, },
|
|
|
- [CIP_SFC_48000] = { 48000, 8, },
|
|
|
- [CIP_SFC_88200] = { 88200, 16, },
|
|
|
- [CIP_SFC_96000] = { 96000, 16, },
|
|
|
- [CIP_SFC_176400] = { 176400, 32, },
|
|
|
- [CIP_SFC_192000] = { 192000, 32, },
|
|
|
+ static const unsigned int rates[] = {
|
|
|
+ [CIP_SFC_32000] = 32000,
|
|
|
+ [CIP_SFC_44100] = 44100,
|
|
|
+ [CIP_SFC_48000] = 48000,
|
|
|
+ [CIP_SFC_88200] = 88200,
|
|
|
+ [CIP_SFC_96000] = 96000,
|
|
|
+ [CIP_SFC_176400] = 176400,
|
|
|
+ [CIP_SFC_192000] = 192000,
|
|
|
};
|
|
|
unsigned int sfc;
|
|
|
|
|
|
if (WARN_ON(amdtp_out_stream_running(s)))
|
|
|
return;
|
|
|
|
|
|
- for (sfc = 0; sfc < ARRAY_SIZE(rate_info); ++sfc)
|
|
|
- if (rate_info[sfc].rate == rate)
|
|
|
+ for (sfc = 0; sfc < CIP_SFC_COUNT; ++sfc)
|
|
|
+ if (rates[sfc] == rate)
|
|
|
goto sfc_found;
|
|
|
WARN_ON(1);
|
|
|
return;
|
|
|
|
|
|
sfc_found:
|
|
|
+ s->dual_wire = (s->flags & CIP_HI_DUALWIRE) && sfc > CIP_SFC_96000;
|
|
|
+ if (s->dual_wire) {
|
|
|
+ sfc -= 2;
|
|
|
+ rate /= 2;
|
|
|
+ pcm_channels *= 2;
|
|
|
+ }
|
|
|
s->sfc = sfc;
|
|
|
- s->syt_interval = rate_info[sfc].syt_interval;
|
|
|
+ s->data_block_quadlets = pcm_channels + DIV_ROUND_UP(midi_ports, 8);
|
|
|
+ s->pcm_channels = pcm_channels;
|
|
|
+ s->midi_ports = midi_ports;
|
|
|
+
|
|
|
+ s->syt_interval = amdtp_syt_intervals[sfc];
|
|
|
|
|
|
/* default buffering in the device */
|
|
|
s->transfer_delay = TRANSFER_DELAY_TICKS - TICKS_PER_CYCLE;
|
|
@@ -108,21 +132,17 @@ sfc_found:
|
|
|
/* additional buffering needed to adjust for no-data packets */
|
|
|
s->transfer_delay += TICKS_PER_SECOND * s->syt_interval / rate;
|
|
|
}
|
|
|
-EXPORT_SYMBOL(amdtp_out_stream_set_rate);
|
|
|
+EXPORT_SYMBOL(amdtp_out_stream_set_parameters);
|
|
|
|
|
|
/**
|
|
|
* amdtp_out_stream_get_max_payload - get the stream's packet size
|
|
|
* @s: the AMDTP output stream
|
|
|
*
|
|
|
* This function must not be called before the stream has been configured
|
|
|
- * with amdtp_out_stream_set_rate(), amdtp_out_stream_set_pcm(), and
|
|
|
- * amdtp_out_stream_set_midi().
|
|
|
+ * with amdtp_out_stream_set_parameters().
|
|
|
*/
|
|
|
unsigned int amdtp_out_stream_get_max_payload(struct amdtp_out_stream *s)
|
|
|
{
|
|
|
- s->data_block_quadlets = s->pcm_channels;
|
|
|
- s->data_block_quadlets += DIV_ROUND_UP(s->midi_ports, 8);
|
|
|
-
|
|
|
return 8 + s->syt_interval * s->data_block_quadlets * 4;
|
|
|
}
|
|
|
EXPORT_SYMBOL(amdtp_out_stream_get_max_payload);
|
|
@@ -133,14 +153,21 @@ static void amdtp_write_s16(struct amdtp_out_stream *s,
|
|
|
static void amdtp_write_s32(struct amdtp_out_stream *s,
|
|
|
struct snd_pcm_substream *pcm,
|
|
|
__be32 *buffer, unsigned int frames);
|
|
|
+static void amdtp_write_s16_dualwire(struct amdtp_out_stream *s,
|
|
|
+ struct snd_pcm_substream *pcm,
|
|
|
+ __be32 *buffer, unsigned int frames);
|
|
|
+static void amdtp_write_s32_dualwire(struct amdtp_out_stream *s,
|
|
|
+ struct snd_pcm_substream *pcm,
|
|
|
+ __be32 *buffer, unsigned int frames);
|
|
|
|
|
|
/**
|
|
|
* amdtp_out_stream_set_pcm_format - set the PCM format
|
|
|
* @s: the AMDTP output stream to configure
|
|
|
* @format: the format of the ALSA PCM device
|
|
|
*
|
|
|
- * The sample format must be set before the stream is started, and must not be
|
|
|
- * changed while the stream is running.
|
|
|
+ * The sample format must be set after the other paramters (rate/PCM channels/
|
|
|
+ * MIDI) and before the stream is started, and must not be changed while the
|
|
|
+ * stream is running.
|
|
|
*/
|
|
|
void amdtp_out_stream_set_pcm_format(struct amdtp_out_stream *s,
|
|
|
snd_pcm_format_t format)
|
|
@@ -153,10 +180,16 @@ void amdtp_out_stream_set_pcm_format(struct amdtp_out_stream *s,
|
|
|
WARN_ON(1);
|
|
|
/* fall through */
|
|
|
case SNDRV_PCM_FORMAT_S16:
|
|
|
- s->transfer_samples = amdtp_write_s16;
|
|
|
+ if (s->dual_wire)
|
|
|
+ s->transfer_samples = amdtp_write_s16_dualwire;
|
|
|
+ else
|
|
|
+ s->transfer_samples = amdtp_write_s16;
|
|
|
break;
|
|
|
case SNDRV_PCM_FORMAT_S32:
|
|
|
- s->transfer_samples = amdtp_write_s32;
|
|
|
+ if (s->dual_wire)
|
|
|
+ s->transfer_samples = amdtp_write_s32_dualwire;
|
|
|
+ else
|
|
|
+ s->transfer_samples = amdtp_write_s32;
|
|
|
break;
|
|
|
}
|
|
|
}
|
|
@@ -305,6 +338,68 @@ static void amdtp_write_s16(struct amdtp_out_stream *s,
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+static void amdtp_write_s32_dualwire(struct amdtp_out_stream *s,
|
|
|
+ struct snd_pcm_substream *pcm,
|
|
|
+ __be32 *buffer, unsigned int frames)
|
|
|
+{
|
|
|
+ struct snd_pcm_runtime *runtime = pcm->runtime;
|
|
|
+ unsigned int channels, frame_adjust_1, frame_adjust_2, 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);
|
|
|
+
|
|
|
+ channels /= 2;
|
|
|
+ for (i = 0; i < frames; ++i) {
|
|
|
+ for (c = 0; c < channels; ++c) {
|
|
|
+ *buffer = cpu_to_be32((*src >> 8) | 0x40000000);
|
|
|
+ src++;
|
|
|
+ buffer += 2;
|
|
|
+ }
|
|
|
+ buffer -= frame_adjust_1;
|
|
|
+ for (c = 0; c < channels; ++c) {
|
|
|
+ *buffer = cpu_to_be32((*src >> 8) | 0x40000000);
|
|
|
+ src++;
|
|
|
+ buffer += 2;
|
|
|
+ }
|
|
|
+ buffer -= frame_adjust_2;
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+static void amdtp_write_s16_dualwire(struct amdtp_out_stream *s,
|
|
|
+ struct snd_pcm_substream *pcm,
|
|
|
+ __be32 *buffer, unsigned int frames)
|
|
|
+{
|
|
|
+ struct snd_pcm_runtime *runtime = pcm->runtime;
|
|
|
+ unsigned int channels, frame_adjust_1, frame_adjust_2, 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);
|
|
|
+
|
|
|
+ channels /= 2;
|
|
|
+ for (i = 0; i < frames; ++i) {
|
|
|
+ for (c = 0; c < channels; ++c) {
|
|
|
+ *buffer = cpu_to_be32((*src << 8) | 0x40000000);
|
|
|
+ src++;
|
|
|
+ buffer += 2;
|
|
|
+ }
|
|
|
+ buffer -= frame_adjust_1;
|
|
|
+ for (c = 0; c < channels; ++c) {
|
|
|
+ *buffer = cpu_to_be32((*src << 8) | 0x40000000);
|
|
|
+ src++;
|
|
|
+ buffer += 2;
|
|
|
+ }
|
|
|
+ buffer -= frame_adjust_2;
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
static void amdtp_fill_pcm_silence(struct amdtp_out_stream *s,
|
|
|
__be32 *buffer, unsigned int frames)
|
|
|
{
|
|
@@ -390,6 +485,9 @@ static void queue_out_packet(struct amdtp_out_stream *s, unsigned int cycle)
|
|
|
s->packet_index = index;
|
|
|
|
|
|
if (pcm) {
|
|
|
+ if (s->dual_wire)
|
|
|
+ data_blocks *= 2;
|
|
|
+
|
|
|
ptr = s->pcm_buffer_pointer + data_blocks;
|
|
|
if (ptr >= pcm->runtime->buffer_size)
|
|
|
ptr -= pcm->runtime->buffer_size;
|
|
@@ -459,8 +557,7 @@ static int queue_initial_skip_packets(struct amdtp_out_stream *s)
|
|
|
* @speed: firewire speed code
|
|
|
*
|
|
|
* The stream cannot be started until it has been configured with
|
|
|
- * amdtp_out_stream_set_rate(), amdtp_out_stream_set_pcm(),
|
|
|
- * amdtp_out_stream_set_midi(), and amdtp_out_stream_set_format();
|
|
|
+ * amdtp_out_stream_set_parameters() and amdtp_out_stream_set_pcm_format(),
|
|
|
* and it must be started before any PCM or MIDI device can be started.
|
|
|
*/
|
|
|
int amdtp_out_stream_start(struct amdtp_out_stream *s, int channel, int speed)
|