ALSA: firewire-lib: Add some AV/C general commands
authorTakashi Sakamoto <o-takashi@sakamocchi.jp>
Fri, 25 Apr 2014 13:44:59 +0000 (22:44 +0900)
committerTakashi Iwai <tiwai@suse.de>
Mon, 26 May 2014 12:23:13 +0000 (14:23 +0200)
This commit adds three commands, which may be used by some firewire device
drivers. These commands are defined in 'AV/C Digital Interface Command Set
General Specification Version 4.2 (2004006, 1394TA)'.

1. PLUG INFO command (clause 10.1)
2. INPUT PLUG SIGNAL FORMAT command (clause 10.10)
3. OUTPUT PLUG SIGNAL FORMAT command (clause 10.11)

By the command 1, the drivers can get the number of plugs for AV/C unit or
subunit.
By the command 2 and 3, the drivers can get/set sampling frequency.

The 'firewire-speakers' already uses INPUT PLUG SIGNAL FORMAT command to set
sampling rate. So this commit also affects the driver.

Signed-off-by: Takashi Sakamoto <o-takashi@sakamocchi.jp>
Signed-off-by: Takashi Iwai <tiwai@suse.de>
sound/firewire/amdtp.c
sound/firewire/amdtp.h
sound/firewire/fcp.c
sound/firewire/fcp.h
sound/firewire/speakers.c

index 7c814ace9e58535a1b7f65a8ae6c08a62286d95e..5b88461234744264150e77c662f5ec6c3b387a8d 100644 (file)
@@ -105,6 +105,17 @@ const unsigned int amdtp_syt_intervals[CIP_SFC_COUNT] = {
 };
 EXPORT_SYMBOL(amdtp_syt_intervals);
 
+const unsigned int amdtp_rate_table[] = {
+       [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,
+};
+EXPORT_SYMBOL(amdtp_rate_table);
+
 /**
  * amdtp_stream_add_pcm_hw_constraints - add hw constraints for PCM substream
  * @s:         the AMDTP stream, which must be initialized.
@@ -176,15 +187,6 @@ void amdtp_stream_set_parameters(struct amdtp_stream *s,
                                 unsigned int pcm_channels,
                                 unsigned int midi_ports)
 {
-       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 i, sfc, midi_channels;
 
        midi_channels = DIV_ROUND_UP(midi_ports, 8);
@@ -194,8 +196,8 @@ void amdtp_stream_set_parameters(struct amdtp_stream *s,
            WARN_ON(midi_channels > AMDTP_MAX_CHANNELS_FOR_MIDI))
                return;
 
-       for (sfc = 0; sfc < CIP_SFC_COUNT; ++sfc)
-               if (rates[sfc] == rate)
+       for (sfc = 0; sfc < sizeof(amdtp_rate_table); ++sfc)
+               if (amdtp_rate_table[sfc] == rate)
                        goto sfc_found;
        WARN_ON(1);
        return;
index d6bb7eb86f0f33f382043e43b2bede99b84ff662..3de34639b1c7fc5a42b73cd0c594ec6e44a91a5b 100644 (file)
@@ -140,6 +140,7 @@ unsigned long amdtp_stream_pcm_pointer(struct amdtp_stream *s);
 void amdtp_stream_pcm_abort(struct amdtp_stream *s);
 
 extern const unsigned int amdtp_syt_intervals[CIP_SFC_COUNT];
+extern const unsigned int amdtp_rate_table[CIP_SFC_COUNT];
 
 /**
  * amdtp_stream_running - check stream is running or not
index 6876bfa9f27d4dce07d1da3861c242183d250e3f..d48e326567c8bb18566550f7d79d98763d349980 100644 (file)
 #include <linux/firewire-constants.h>
 #include <linux/list.h>
 #include <linux/module.h>
+#include <linux/slab.h>
 #include <linux/sched.h>
 #include <linux/spinlock.h>
 #include <linux/wait.h>
 #include <linux/delay.h>
 #include "fcp.h"
 #include "lib.h"
+#include "amdtp.h"
 
 #define CTS_AVC 0x00
 
 #define ERROR_DELAY_MS 5
 #define FCP_TIMEOUT_MS 125
 
+int avc_general_set_sig_fmt(struct fw_unit *unit, unsigned int rate,
+                           enum avc_general_plug_dir dir,
+                           unsigned short pid)
+{
+       unsigned int sfc;
+       u8 *buf;
+       bool flag;
+       int err;
+
+       flag = false;
+       for (sfc = 0; sfc < CIP_SFC_COUNT; sfc++) {
+               if (amdtp_rate_table[sfc] == rate) {
+                       flag = true;
+                       break;
+               }
+       }
+       if (!flag)
+               return -EINVAL;
+
+       buf = kzalloc(8, GFP_KERNEL);
+       if (buf == NULL)
+               return -ENOMEM;
+
+       buf[0] = 0x00;          /* AV/C CONTROL */
+       buf[1] = 0xff;          /* UNIT */
+       if (dir == AVC_GENERAL_PLUG_DIR_IN)
+               buf[2] = 0x19;  /* INPUT PLUG SIGNAL FORMAT */
+       else
+               buf[2] = 0x18;  /* OUTPUT PLUG SIGNAL FORMAT */
+       buf[3] = 0xff & pid;    /* plug id */
+       buf[4] = 0x90;          /* EOH_1, Form_1, FMT. AM824 */
+       buf[5] = 0x07 & sfc;    /* FDF-hi. AM824, frequency */
+       buf[6] = 0xff;          /* FDF-mid. AM824, SYT hi (not used)*/
+       buf[7] = 0xff;          /* FDF-low. AM824, SYT lo (not used) */
+
+       /* do transaction and check buf[1-5] are the same against command */
+       err = fcp_avc_transaction(unit, buf, 8, buf, 8,
+                                 BIT(1) | BIT(2) | BIT(3) | BIT(4) | BIT(5));
+       if (err >= 0 && err < 8)
+               err = -EIO;
+       else if (buf[0] == 0x08) /* NOT IMPLEMENTED */
+               err = -ENOSYS;
+       else if (buf[0] == 0x0a) /* REJECTED */
+               err = -EINVAL;
+       if (err < 0)
+               goto end;
+
+       err = 0;
+end:
+       kfree(buf);
+       return err;
+}
+EXPORT_SYMBOL(avc_general_set_sig_fmt);
+
+int avc_general_get_sig_fmt(struct fw_unit *unit, unsigned int *rate,
+                           enum avc_general_plug_dir dir,
+                           unsigned short pid)
+{
+       unsigned int sfc;
+       u8 *buf;
+       int err;
+
+       buf = kzalloc(8, GFP_KERNEL);
+       if (buf == NULL)
+               return -ENOMEM;
+
+       buf[0] = 0x01;          /* AV/C STATUS */
+       buf[1] = 0xff;          /* Unit */
+       if (dir == AVC_GENERAL_PLUG_DIR_IN)
+               buf[2] = 0x19;  /* INPUT PLUG SIGNAL FORMAT */
+       else
+               buf[2] = 0x18;  /* OUTPUT PLUG SIGNAL FORMAT */
+       buf[3] = 0xff & pid;    /* plug id */
+       buf[4] = 0x90;          /* EOH_1, Form_1, FMT. AM824 */
+       buf[5] = 0xff;          /* FDF-hi. AM824, frequency */
+       buf[6] = 0xff;          /* FDF-mid. AM824, SYT hi (not used) */
+       buf[7] = 0xff;          /* FDF-low. AM824, SYT lo (not used) */
+
+       /* do transaction and check buf[1-4] are the same against command */
+       err = fcp_avc_transaction(unit, buf, 8, buf, 8,
+                                 BIT(1) | BIT(2) | BIT(3) | BIT(4));
+       if (err >= 0 && err < 8)
+               err = -EIO;
+       else if (buf[0] == 0x08) /* NOT IMPLEMENTED */
+               err = -ENOSYS;
+       else if (buf[0] == 0x0a) /* REJECTED */
+               err = -EINVAL;
+       else if (buf[0] == 0x0b) /* IN TRANSITION */
+               err = -EAGAIN;
+       if (err < 0)
+               goto end;
+
+       /* check sfc field and pick up rate */
+       sfc = 0x07 & buf[5];
+       if (sfc >= CIP_SFC_COUNT) {
+               err = -EAGAIN;  /* also in transition */
+               goto end;
+       }
+
+       *rate = amdtp_rate_table[sfc];
+       err = 0;
+end:
+       kfree(buf);
+       return err;
+}
+EXPORT_SYMBOL(avc_general_get_sig_fmt);
+
+int avc_general_get_plug_info(struct fw_unit *unit, unsigned int subunit_type,
+                             unsigned int subunit_id, unsigned int subfunction,
+                             u8 info[AVC_PLUG_INFO_BUF_BYTES])
+{
+       u8 *buf;
+       int err;
+
+       /* extended subunit in spec.4.2 is not supported */
+       if ((subunit_type == 0x1E) || (subunit_id == 5))
+               return -EINVAL;
+
+       buf = kzalloc(8, GFP_KERNEL);
+       if (buf == NULL)
+               return -ENOMEM;
+
+       buf[0] = 0x01;  /* AV/C STATUS */
+       /* UNIT or Subunit, Functionblock */
+       buf[1] = ((subunit_type & 0x1f) << 3) | (subunit_id & 0x7);
+       buf[2] = 0x02;  /* PLUG INFO */
+       buf[3] = 0xff & subfunction;
+
+       err = fcp_avc_transaction(unit, buf, 8, buf, 8, BIT(1) | BIT(2));
+       if (err >= 0 && err < 8)
+               err = -EIO;
+       else if (buf[0] == 0x08) /* NOT IMPLEMENTED */
+               err = -ENOSYS;
+       else if (buf[0] == 0x0a) /* REJECTED */
+               err = -EINVAL;
+       else if (buf[0] == 0x0b) /* IN TRANSITION */
+               err = -EAGAIN;
+       if (err < 0)
+               goto end;
+
+       info[0] = buf[4];
+       info[1] = buf[5];
+       info[2] = buf[6];
+       info[3] = buf[7];
+
+       err = 0;
+end:
+       kfree(buf);
+       return err;
+}
+EXPORT_SYMBOL(avc_general_get_plug_info);
+
 static DEFINE_SPINLOCK(transactions_lock);
 static LIST_HEAD(transactions);
 
index 86595688bd91c2cb3227c2e34dd97e92a7973805..63ae4f7ce3af9b72ee3a7edfc969585044444719 100644 (file)
@@ -1,8 +1,29 @@
 #ifndef SOUND_FIREWIRE_FCP_H_INCLUDED
 #define SOUND_FIREWIRE_FCP_H_INCLUDED
 
+#define        AVC_PLUG_INFO_BUF_BYTES 4
+
 struct fw_unit;
 
+/*
+ * AV/C Digital Interface Command Set General Specification 4.2
+ * (Sep 2004, 1394TA)
+ */
+enum avc_general_plug_dir {
+       AVC_GENERAL_PLUG_DIR_IN         = 0,
+       AVC_GENERAL_PLUG_DIR_OUT        = 1,
+       AVC_GENERAL_PLUG_DIR_COUNT
+};
+int avc_general_set_sig_fmt(struct fw_unit *unit, unsigned int rate,
+                           enum avc_general_plug_dir dir,
+                           unsigned short plug);
+int avc_general_get_sig_fmt(struct fw_unit *unit, unsigned int *rate,
+                           enum avc_general_plug_dir dir,
+                           unsigned short plug);
+int avc_general_get_plug_info(struct fw_unit *unit, unsigned int subunit_type,
+                             unsigned int subunit_id, unsigned int subfunction,
+                             u8 info[AVC_PLUG_INFO_BUF_BYTES]);
+
 int fcp_avc_transaction(struct fw_unit *unit,
                        const void *command, unsigned int command_size,
                        void *response, unsigned int response_size,
index dd9983b98607da4859aa56a7bdba4c7e8275403c..768d40ddfebbe63cd035089dbcdffd6e66119472 100644 (file)
@@ -187,42 +187,6 @@ static void fwspk_stop_stream(struct fwspk *fwspk)
        }
 }
 
-static int fwspk_set_rate(struct fwspk *fwspk, unsigned int sfc)
-{
-       u8 *buf;
-       int err;
-
-       buf = kmalloc(8, GFP_KERNEL);
-       if (!buf)
-               return -ENOMEM;
-
-       buf[0] = 0x00;          /* AV/C, CONTROL */
-       buf[1] = 0xff;          /* unit */
-       buf[2] = 0x19;          /* INPUT PLUG SIGNAL FORMAT */
-       buf[3] = 0x00;          /* plug 0 */
-       buf[4] = 0x90;          /* format: audio */
-       buf[5] = 0x00 | sfc;    /* AM824, frequency */
-       buf[6] = 0xff;          /* SYT (not used) */
-       buf[7] = 0xff;
-
-       err = fcp_avc_transaction(fwspk->unit, buf, 8, buf, 8,
-                                 BIT(1) | BIT(2) | BIT(3) | BIT(4) | BIT(5));
-       if (err < 0)
-               goto error;
-       if (err < 6 || buf[0] != 0x09 /* ACCEPTED */) {
-               dev_err(&fwspk->unit->device, "failed to set sample rate\n");
-               err = -EIO;
-               goto error;
-       }
-
-       err = 0;
-
-error:
-       kfree(buf);
-
-       return err;
-}
-
 static int fwspk_hw_params(struct snd_pcm_substream *substream,
                           struct snd_pcm_hw_params *hw_params)
 {
@@ -246,9 +210,12 @@ static int fwspk_hw_params(struct snd_pcm_substream *substream,
        amdtp_stream_set_pcm_format(&fwspk->stream,
                                    params_format(hw_params));
 
-       err = fwspk_set_rate(fwspk, fwspk->stream.sfc);
-       if (err < 0)
+       err = avc_general_set_sig_fmt(fwspk->unit, params_rate(hw_params),
+                                     AVC_GENERAL_PLUG_DIR_IN, 0);
+       if (err < 0) {
+               dev_err(&fwspk->unit->device, "failed to set sample rate\n");
                goto err_buffer;
+       }
 
        return 0;