Merge remote-tracking branch 'iommu/next'
[deliverable/linux.git] / sound / firewire / dice / dice-pcm.c
index a5c9b58655ef2f69141a66e440480ac4f6049bc6..6074fe1f00f790f971d3a9b3722d330fe38c34b9 100644 (file)
 
 static int limit_channels_and_rates(struct snd_dice *dice,
                                    struct snd_pcm_runtime *runtime,
-                                   struct amdtp_stream *stream)
+                                   enum amdtp_stream_direction dir,
+                                   unsigned int index, unsigned int size)
 {
        struct snd_pcm_hardware *hw = &runtime->hw;
+       struct amdtp_stream *stream;
        unsigned int rate;
-       __be32 reg[2];
+       __be32 reg;
        int err;
 
        /*
         * Retrieve current Multi Bit Linear Audio data channel and limit to
         * it.
         */
-       if (stream == &dice->tx_stream) {
-               err = snd_dice_transaction_read_tx(dice, TX_NUMBER_AUDIO,
-                                                  reg, sizeof(reg));
+       if (dir == AMDTP_IN_STREAM) {
+               stream = &dice->tx_stream[index];
+               err = snd_dice_transaction_read_tx(dice,
+                               size * index + TX_NUMBER_AUDIO,
+                               &reg, sizeof(reg));
        } else {
-               err = snd_dice_transaction_read_rx(dice, RX_NUMBER_AUDIO,
-                                                  reg, sizeof(reg));
+               stream = &dice->rx_stream[index];
+               err = snd_dice_transaction_read_rx(dice,
+                               size * index + RX_NUMBER_AUDIO,
+                               &reg, sizeof(reg));
        }
        if (err < 0)
                return err;
 
-       hw->channels_min = hw->channels_max = be32_to_cpu(reg[0]);
+       hw->channels_min = hw->channels_max = be32_to_cpu(reg);
 
        /* Retrieve current sampling transfer frequency and limit to it. */
        err = snd_dice_transaction_get_rate(dice, &rate);
@@ -62,7 +68,10 @@ static int init_hw_info(struct snd_dice *dice,
 {
        struct snd_pcm_runtime *runtime = substream->runtime;
        struct snd_pcm_hardware *hw = &runtime->hw;
+       enum amdtp_stream_direction dir;
        struct amdtp_stream *stream;
+       __be32 reg[2];
+       unsigned int count, size;
        int err;
 
        hw->info = SNDRV_PCM_INFO_MMAP |
@@ -74,13 +83,28 @@ static int init_hw_info(struct snd_dice *dice,
 
        if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) {
                hw->formats = AM824_IN_PCM_FORMAT_BITS;
-               stream = &dice->tx_stream;
+               dir = AMDTP_IN_STREAM;
+               stream = &dice->tx_stream[substream->pcm->device];
+               err = snd_dice_transaction_read_tx(dice, TX_NUMBER, reg,
+                                                  sizeof(reg));
        } else {
                hw->formats = AM824_OUT_PCM_FORMAT_BITS;
-               stream = &dice->rx_stream;
+               dir = AMDTP_OUT_STREAM;
+               stream = &dice->rx_stream[substream->pcm->device];
+               err = snd_dice_transaction_read_rx(dice, RX_NUMBER, reg,
+                                                  sizeof(reg));
        }
 
-       err = limit_channels_and_rates(dice, runtime, stream);
+       if (err < 0)
+               return err;
+
+       count = min_t(unsigned int, be32_to_cpu(reg[0]), MAX_STREAMS);
+       if (substream->pcm->device >= count)
+               return -ENXIO;
+
+       size = be32_to_cpu(reg[1]) * 4;
+       err = limit_channels_and_rates(dice, substream->runtime, dir,
+                                      substream->pcm->device, size);
        if (err < 0)
                return err;
        limit_period_and_buffer(hw);
@@ -122,6 +146,7 @@ static int capture_hw_params(struct snd_pcm_substream *substream,
                             struct snd_pcm_hw_params *hw_params)
 {
        struct snd_dice *dice = substream->private_data;
+       struct amdtp_stream *stream = &dice->tx_stream[substream->pcm->device];
        int err;
 
        err = snd_pcm_lib_alloc_vmalloc_buffer(substream,
@@ -135,7 +160,7 @@ static int capture_hw_params(struct snd_pcm_substream *substream,
                mutex_unlock(&dice->mutex);
        }
 
-       amdtp_am824_set_pcm_format(&dice->tx_stream, params_format(hw_params));
+       amdtp_am824_set_pcm_format(stream, params_format(hw_params));
 
        return 0;
 }
@@ -143,6 +168,7 @@ static int playback_hw_params(struct snd_pcm_substream *substream,
                              struct snd_pcm_hw_params *hw_params)
 {
        struct snd_dice *dice = substream->private_data;
+       struct amdtp_stream *stream = &dice->rx_stream[substream->pcm->device];
        int err;
 
        err = snd_pcm_lib_alloc_vmalloc_buffer(substream,
@@ -156,7 +182,7 @@ static int playback_hw_params(struct snd_pcm_substream *substream,
                mutex_unlock(&dice->mutex);
        }
 
-       amdtp_am824_set_pcm_format(&dice->rx_stream, params_format(hw_params));
+       amdtp_am824_set_pcm_format(stream, params_format(hw_params));
 
        return 0;
 }
@@ -196,26 +222,28 @@ static int playback_hw_free(struct snd_pcm_substream *substream)
 static int capture_prepare(struct snd_pcm_substream *substream)
 {
        struct snd_dice *dice = substream->private_data;
+       struct amdtp_stream *stream = &dice->tx_stream[substream->pcm->device];
        int err;
 
        mutex_lock(&dice->mutex);
        err = snd_dice_stream_start_duplex(dice, substream->runtime->rate);
        mutex_unlock(&dice->mutex);
        if (err >= 0)
-               amdtp_stream_pcm_prepare(&dice->tx_stream);
+               amdtp_stream_pcm_prepare(stream);
 
        return 0;
 }
 static int playback_prepare(struct snd_pcm_substream *substream)
 {
        struct snd_dice *dice = substream->private_data;
+       struct amdtp_stream *stream = &dice->rx_stream[substream->pcm->device];
        int err;
 
        mutex_lock(&dice->mutex);
        err = snd_dice_stream_start_duplex(dice, substream->runtime->rate);
        mutex_unlock(&dice->mutex);
        if (err >= 0)
-               amdtp_stream_pcm_prepare(&dice->rx_stream);
+               amdtp_stream_pcm_prepare(stream);
 
        return err;
 }
@@ -223,13 +251,14 @@ static int playback_prepare(struct snd_pcm_substream *substream)
 static int capture_trigger(struct snd_pcm_substream *substream, int cmd)
 {
        struct snd_dice *dice = substream->private_data;
+       struct amdtp_stream *stream = &dice->tx_stream[substream->pcm->device];
 
        switch (cmd) {
        case SNDRV_PCM_TRIGGER_START:
-               amdtp_stream_pcm_trigger(&dice->tx_stream, substream);
+               amdtp_stream_pcm_trigger(stream, substream);
                break;
        case SNDRV_PCM_TRIGGER_STOP:
-               amdtp_stream_pcm_trigger(&dice->tx_stream, NULL);
+               amdtp_stream_pcm_trigger(stream, NULL);
                break;
        default:
                return -EINVAL;
@@ -240,13 +269,14 @@ static int capture_trigger(struct snd_pcm_substream *substream, int cmd)
 static int playback_trigger(struct snd_pcm_substream *substream, int cmd)
 {
        struct snd_dice *dice = substream->private_data;
+       struct amdtp_stream *stream = &dice->rx_stream[substream->pcm->device];
 
        switch (cmd) {
        case SNDRV_PCM_TRIGGER_START:
-               amdtp_stream_pcm_trigger(&dice->rx_stream, substream);
+               amdtp_stream_pcm_trigger(stream, substream);
                break;
        case SNDRV_PCM_TRIGGER_STOP:
-               amdtp_stream_pcm_trigger(&dice->rx_stream, NULL);
+               amdtp_stream_pcm_trigger(stream, NULL);
                break;
        default:
                return -EINVAL;
@@ -258,19 +288,21 @@ static int playback_trigger(struct snd_pcm_substream *substream, int cmd)
 static snd_pcm_uframes_t capture_pointer(struct snd_pcm_substream *substream)
 {
        struct snd_dice *dice = substream->private_data;
+       struct amdtp_stream *stream = &dice->tx_stream[substream->pcm->device];
 
-       return amdtp_stream_pcm_pointer(&dice->tx_stream);
+       return amdtp_stream_pcm_pointer(stream);
 }
 static snd_pcm_uframes_t playback_pointer(struct snd_pcm_substream *substream)
 {
        struct snd_dice *dice = substream->private_data;
+       struct amdtp_stream *stream = &dice->rx_stream[substream->pcm->device];
 
-       return amdtp_stream_pcm_pointer(&dice->rx_stream);
+       return amdtp_stream_pcm_pointer(stream);
 }
 
 int snd_dice_create_pcm(struct snd_dice *dice)
 {
-       static struct snd_pcm_ops capture_ops = {
+       static const struct snd_pcm_ops capture_ops = {
                .open      = pcm_open,
                .close     = pcm_close,
                .ioctl     = snd_pcm_lib_ioctl,
@@ -282,7 +314,7 @@ int snd_dice_create_pcm(struct snd_dice *dice)
                .page      = snd_pcm_lib_get_vmalloc_page,
                .mmap      = snd_pcm_lib_mmap_vmalloc,
        };
-       static struct snd_pcm_ops playback_ops = {
+       static const struct snd_pcm_ops playback_ops = {
                .open      = pcm_open,
                .close     = pcm_close,
                .ioctl     = snd_pcm_lib_ioctl,
@@ -296,40 +328,51 @@ int snd_dice_create_pcm(struct snd_dice *dice)
        };
        __be32 reg;
        struct snd_pcm *pcm;
-       unsigned int capture, playback;
+       unsigned int i, max_capture, max_playback, capture, playback;
        int err;
 
-       /*
-        * Check whether PCM substreams are required.
-        *
-        * TODO: in the case that any PCM substreams are not avail at a certain
-        * sampling transfer frequency?
-        */
-       err = snd_dice_transaction_read_tx(dice, TX_NUMBER_AUDIO,
-                                          &reg, sizeof(reg));
-       if (err < 0)
-               return err;
-       if (be32_to_cpu(reg) > 0)
-               capture = 1;
-
-       err = snd_dice_transaction_read_rx(dice, RX_NUMBER_AUDIO,
-                                          &reg, sizeof(reg));
-       if (err < 0)
-               return err;
-       if (be32_to_cpu(reg) > 0)
-               playback = 1;
-
-       err = snd_pcm_new(dice->card, "DICE", 0, playback, capture, &pcm);
-       if (err < 0)
-               return err;
-       pcm->private_data = dice;
-       strcpy(pcm->name, dice->card->shortname);
-
-       if (capture > 0)
-               snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &capture_ops);
+       /* Check whether PCM substreams are required. */
+       if (dice->force_two_pcms) {
+               max_capture = max_playback = 2;
+       } else {
+               max_capture = max_playback = 0;
+               err = snd_dice_transaction_read_tx(dice, TX_NUMBER, &reg,
+                                                  sizeof(reg));
+               if (err < 0)
+                       return err;
+               max_capture = min_t(unsigned int, be32_to_cpu(reg), MAX_STREAMS);
+
+               err = snd_dice_transaction_read_rx(dice, RX_NUMBER, &reg,
+                                                  sizeof(reg));
+               if (err < 0)
+                       return err;
+               max_playback = min_t(unsigned int, be32_to_cpu(reg), MAX_STREAMS);
+       }
 
-       if (playback > 0)
-               snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &playback_ops);
+       for (i = 0; i < MAX_STREAMS; i++) {
+               capture = playback = 0;
+               if (i < max_capture)
+                       capture = 1;
+               if (i < max_playback)
+                       playback = 1;
+               if (capture == 0 && playback == 0)
+                       break;
+
+               err = snd_pcm_new(dice->card, "DICE", i, playback, capture,
+                                 &pcm);
+               if (err < 0)
+                       return err;
+               pcm->private_data = dice;
+               strcpy(pcm->name, dice->card->shortname);
+
+               if (capture > 0)
+                       snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE,
+                                       &capture_ops);
+
+               if (playback > 0)
+                       snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK,
+                                       &playback_ops);
+       }
 
        return 0;
 }
This page took 0.027604 seconds and 5 git commands to generate.