]> Pileus Git - ~andy/linux/blobdiff - sound/firewire/amdtp.c
ALSA: dice: support dual-wire stream format at 192 kHz
[~andy/linux] / sound / firewire / amdtp.c
index d56b8e736b7d8097575c59792c70c0a2830ed7d2..a09c3b393c0bc7960106581d1324a03e043c5593 100644 (file)
@@ -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)