ALSA: dice: get rate-dependent parameters
authorClemens Ladisch <clemens@ladisch.de>
Sun, 4 Dec 2011 21:23:59 +0000 (22:23 +0100)
committerClemens Ladisch <clemens@ladisch.de>
Sun, 20 Oct 2013 20:07:57 +0000 (22:07 +0200)
In preparation for sample rate selection support, read the stream
parameters that might change when running at different sample rates.

Signed-off-by: Clemens Ladisch <clemens@ladisch.de>
sound/firewire/dice.c

index 49b47ba09cddfd7501692b182faf10056ec83d78..e6bba6d32bd880dcd53362b697b1b2c60f5cf38c 100644 (file)
@@ -6,10 +6,12 @@
  */
 
 #include <linux/compat.h>
+#include <linux/completion.h>
 #include <linux/delay.h>
 #include <linux/device.h>
 #include <linux/firewire.h>
 #include <linux/firewire-constants.h>
+#include <linux/jiffies.h>
 #include <linux/module.h>
 #include <linux/mod_devicetable.h>
 #include <linux/mutex.h>
@@ -37,11 +39,14 @@ struct dice {
        unsigned int global_offset;
        unsigned int rx_offset;
        unsigned int clock_caps;
+       unsigned int rx_channels[3];
+       unsigned int rx_midi_ports[3];
        struct fw_address_handler notification_handler;
        int owner_generation;
        int dev_lock_count; /* > 0 driver, < 0 userspace */
        bool dev_lock_changed;
        bool global_enabled;
+       struct completion clock_accepted;
        wait_queue_head_t hwdep_wait;
        u32 notification_bits;
        struct fw_iso_resources resources;
@@ -53,15 +58,23 @@ MODULE_AUTHOR("Clemens Ladisch <clemens@ladisch.de>");
 MODULE_LICENSE("GPL v2");
 
 static const unsigned int dice_rates[] = {
+       /* mode 0 */
        [0] =  32000,
        [1] =  44100,
        [2] =  48000,
+       /* mode 1 */
        [3] =  88200,
        [4] =  96000,
+       /* mode 2 */
        [5] = 176400,
        [6] = 192000,
 };
 
+static unsigned int rate_index_to_mode(unsigned int rate_index)
+{
+       return ((int)rate_index - 1) / 2;
+}
+
 static void dice_lock_changed(struct dice *dice)
 {
        dice->dev_lock_changed = true;
@@ -264,6 +277,7 @@ static void dice_notification(struct fw_card *card, struct fw_request *request,
                              void *data, size_t length, void *callback_data)
 {
        struct dice *dice = callback_data;
+       u32 bits;
        unsigned long flags;
 
        if (tcode != TCODE_WRITE_QUADLET_REQUEST) {
@@ -274,10 +288,17 @@ static void dice_notification(struct fw_card *card, struct fw_request *request,
                fw_send_response(card, request, RCODE_ADDRESS_ERROR);
                return;
        }
+
+       bits = be32_to_cpup(data);
+
        spin_lock_irqsave(&dice->lock, flags);
-       dice->notification_bits |= be32_to_cpup(data);
+       dice->notification_bits |= bits;
        spin_unlock_irqrestore(&dice->lock, flags);
+
        fw_send_response(card, request, RCODE_COMPLETE);
+
+       if (bits & NOTIFY_CLOCK_ACCEPTED)
+               complete(&dice->clock_accepted);
        wake_up(&dice->hwdep_wait);
 }
 
@@ -457,6 +478,26 @@ static void dice_stream_stop(struct dice *dice)
        fw_iso_resources_free(&dice->resources);
 }
 
+static int dice_change_rate(struct dice *dice, unsigned int clock_rate)
+{
+       __be32 value;
+       int err;
+
+       INIT_COMPLETION(dice->clock_accepted);
+
+       value = cpu_to_be32(clock_rate | CLOCK_SOURCE_ARX1);
+       err = snd_fw_transaction(dice->unit, TCODE_WRITE_QUADLET_REQUEST,
+                                global_address(dice, GLOBAL_CLOCK_SELECT),
+                                &value, 4, 0);
+       if (err < 0)
+               return err;
+
+       wait_for_completion_timeout(&dice->clock_accepted,
+                                   msecs_to_jiffies(100));
+
+       return 0;
+}
+
 static int dice_hw_params(struct snd_pcm_substream *substream,
                          struct snd_pcm_hw_params *hw_params)
 {
@@ -831,11 +872,51 @@ static int dice_interface_check(struct fw_unit *unit)
        return 0;
 }
 
+static int highest_supported_mode_rate(struct dice *dice, unsigned int mode)
+{
+       int i;
+
+       for (i = ARRAY_SIZE(dice_rates) - 1; i >= 0; --i)
+               if ((dice->clock_caps & (1 << i)) &&
+                   rate_index_to_mode(i) == mode)
+                       return i;
+
+       return -1;
+}
+
+static int dice_read_mode_params(struct dice *dice, unsigned int mode)
+{
+       __be32 values[2];
+       int rate_index, err;
+
+       rate_index = highest_supported_mode_rate(dice, mode);
+       if (rate_index < 0) {
+               dice->rx_channels[mode] = 0;
+               dice->rx_midi_ports[mode] = 0;
+               return 0;
+       }
+
+       err = dice_change_rate(dice, rate_index << CLOCK_RATE_SHIFT);
+       if (err < 0)
+               return err;
+
+       err = snd_fw_transaction(dice->unit, TCODE_READ_BLOCK_REQUEST,
+                                rx_address(dice, RX_NUMBER_AUDIO),
+                                values, 2 * 4, 0);
+       if (err < 0)
+               return err;
+
+       dice->rx_channels[mode]   = be32_to_cpu(values[0]);
+       dice->rx_midi_ports[mode] = be32_to_cpu(values[1]);
+
+       return 0;
+}
+
 static int dice_read_params(struct dice *dice)
 {
        __be32 pointers[6];
        __be32 value;
-       int err;
+       int mode, err;
 
        err = snd_fw_transaction(dice->unit, TCODE_READ_BLOCK_REQUEST,
                                 DICE_PRIVATE_SPACE,
@@ -863,6 +944,12 @@ static int dice_read_params(struct dice *dice)
                                   CLOCK_CAP_SOURCE_INTERNAL;
        }
 
+       for (mode = 2; mode >= 0; --mode) {
+               err = dice_read_mode_params(dice, mode);
+               if (err < 0)
+                       return err;
+       }
+
        return 0;
 }
 
@@ -922,6 +1009,7 @@ static int dice_probe(struct fw_unit *unit, const struct ieee1394_device_id *id)
        spin_lock_init(&dice->lock);
        mutex_init(&dice->mutex);
        dice->unit = unit;
+       init_completion(&dice->clock_accepted);
        init_waitqueue_head(&dice->hwdep_wait);
 
        dice->notification_handler.length = 4;