ALSA: oxfw: support the case that AV/C Stream Format Information command is not available
authorTakashi Sakamoto <o-takashi@sakamocchi.jp>
Sun, 18 Feb 2024 07:41:26 +0000 (16:41 +0900)
committerTakashi Iwai <tiwai@suse.de>
Mon, 19 Feb 2024 08:24:35 +0000 (09:24 +0100)
Miglia Harmony Audio does neither support AV/C Stream Format Information
command nor AV/C Extended Stream Format Information command.

This commit adds a workaround for the case and uses the hard-coded formats.

Signed-off-by: Takashi Sakamoto <o-takashi@sakamocchi.jp>
Link: https://lore.kernel.org/r/20240218074128.95210-3-o-takashi@sakamocchi.jp
Signed-off-by: Takashi Iwai <tiwai@suse.de>
sound/firewire/oxfw/oxfw-stream.c
sound/firewire/oxfw/oxfw.h

index 6d8722f..40716bb 100644 (file)
@@ -486,26 +486,57 @@ int snd_oxfw_stream_get_current_formation(struct snd_oxfw *oxfw,
                                enum avc_general_plug_dir dir,
                                struct snd_oxfw_stream_formation *formation)
 {
-       u8 *format;
-       unsigned int len;
        int err;
 
-       len = AVC_GENERIC_FRAME_MAXIMUM_BYTES;
-       format = kmalloc(len, GFP_KERNEL);
-       if (format == NULL)
-               return -ENOMEM;
+       if (!(oxfw->quirks & SND_OXFW_QUIRK_STREAM_FORMAT_INFO_UNSUPPORTED)) {
+               u8 *format;
+               unsigned int len;
 
-       err = avc_stream_get_format_single(oxfw->unit, dir, 0, format, &len);
-       if (err < 0)
-               goto end;
-       if (len < 3) {
-               err = -EIO;
-               goto end;
+               len = AVC_GENERIC_FRAME_MAXIMUM_BYTES;
+               format = kmalloc(len, GFP_KERNEL);
+               if (format == NULL)
+                       return -ENOMEM;
+
+               err = avc_stream_get_format_single(oxfw->unit, dir, 0, format, &len);
+               if (err >= 0) {
+                       if (len < 3)
+                               err = -EIO;
+                       else
+                               err = snd_oxfw_stream_parse_format(format, formation);
+               }
+
+               kfree(format);
+       } else {
+               // Miglia Harmony Audio does not support Extended Stream Format Information
+               // command. Use the duplicated hard-coded format, instead.
+               unsigned int rate;
+               u8 *const *formats;
+               int i;
+
+               err = avc_general_get_sig_fmt(oxfw->unit, &rate, dir, 0);
+               if (err < 0)
+                       return err;
+
+               if (dir == AVC_GENERAL_PLUG_DIR_IN)
+                       formats = oxfw->rx_stream_formats;
+               else
+                       formats = oxfw->tx_stream_formats;
+
+               for (i = 0; (i < SND_OXFW_STREAM_FORMAT_ENTRIES); ++i) {
+                       if (!formats[i])
+                               continue;
+
+                       err = snd_oxfw_stream_parse_format(formats[i], formation);
+                       if (err < 0)
+                               continue;
+
+                       if (formation->rate == rate)
+                               break;
+               }
+               if (i == SND_OXFW_STREAM_FORMAT_ENTRIES)
+                       return -EIO;
        }
 
-       err = snd_oxfw_stream_parse_format(format, formation);
-end:
-       kfree(format);
        return err;
 }
 
@@ -600,14 +631,33 @@ assume_stream_formats(struct snd_oxfw *oxfw, enum avc_general_plug_dir dir,
        unsigned int i, eid;
        int err;
 
-       /* get format at current sampling rate */
-       err = avc_stream_get_format_single(oxfw->unit, dir, pid, buf, len);
-       if (err < 0) {
-               dev_err(&oxfw->unit->device,
-               "fail to get current stream format for isoc %s plug %d:%d\n",
-                       (dir == AVC_GENERAL_PLUG_DIR_IN) ? "in" : "out",
-                       pid, err);
-               goto end;
+       // get format at current sampling rate.
+       if (!(oxfw->quirks & SND_OXFW_QUIRK_STREAM_FORMAT_INFO_UNSUPPORTED)) {
+               err = avc_stream_get_format_single(oxfw->unit, dir, pid, buf, len);
+               if (err < 0) {
+                       dev_err(&oxfw->unit->device,
+                               "fail to get current stream format for isoc %s plug %d:%d\n",
+                               (dir == AVC_GENERAL_PLUG_DIR_IN) ? "in" : "out",
+                               pid, err);
+                       goto end;
+               }
+       } else {
+               // Miglia Harmony Audio does not support Extended Stream Format Information
+               // command. Use the hard-coded format, instead.
+               buf[0] = 0x90;
+               buf[1] = 0x40;
+               buf[2] = avc_stream_rate_table[0];
+               buf[3] = 0x00;
+               buf[4] = 0x01;
+
+               if (dir == AVC_GENERAL_PLUG_DIR_IN)
+                       buf[5] = 0x08;
+               else
+                       buf[5] = 0x02;
+
+               buf[6] = 0x06;
+
+               *len = 7;
        }
 
        /* parse and set stream format */
index 39316ae..3bf8d7b 100644 (file)
@@ -52,6 +52,8 @@ enum snd_oxfw_quirk {
        // performs media clock recovery voluntarily. In the recovery, the packets with NO_INFO
        // are ignored, thus driver should transfer packets with timestamp.
        SND_OXFW_QUIRK_VOLUNTARY_RECOVERY = 0x20,
+       // Miglia Harmony Audio does not support AV/C Stream Format Information command.
+       SND_OXFW_QUIRK_STREAM_FORMAT_INFO_UNSUPPORTED = 0x40,
 };
 
 /* This is an arbitrary number for convinience. */