Merge tag 'sound-5.8-rc5' of git://git.kernel.org/pub/scm/linux/kernel/git/tiwai...
authorLinus Torvalds <torvalds@linux-foundation.org>
Wed, 8 Jul 2020 18:07:09 +0000 (11:07 -0700)
committerLinus Torvalds <torvalds@linux-foundation.org>
Wed, 8 Jul 2020 18:07:09 +0000 (11:07 -0700)
Pull sound fixes from Takashi Iwai:
 "A collection of small, mostly device-specific fixes.

  The significant one is the regression fix for USB-audio implicit
  feedback devices due to the incorrect frame size calculation, which
  landed in 5.8 and stable trees.

  In addition, a few usual HD-audio and USB-audio quirks, Intel HDMI
  fixes, ASoC fsl and rt5682 fixes, as well as the fix in
  compress-offload partial drain operation"

* tag 'sound-5.8-rc5' of git://git.kernel.org/pub/scm/linux/kernel/git/tiwai/sound:
  ALSA: compress: fix partial_drain completion state
  ALSA: usb-audio: Add implicit feedback quirk for RTX6001
  ALSA: usb-audio: add quirk for MacroSilicon MS2109
  ALSA: hda/realtek: Enable headset mic of Acer Veriton N4660G with ALC269VC
  ALSA: hda/realtek: Enable headset mic of Acer C20-820 with ALC269VC
  ALSA: hda/realtek - Enable audio jacks of Acer vCopperbox with ALC269VC
  ALSA: hda/realtek - Fix Lenovo Thinkpad X1 Carbon 7th quirk subdevice id
  ALSA: hda/hdmi: improve debug traces for stream lookups
  ALSA: hda/hdmi: fix failures at PCM open on Intel ICL and later
  ALSA: opl3: fix infoleak in opl3
  ALSA: usb-audio: Replace s/frame/packet/ where appropriate
  ALSA: usb-audio: Fix packet size calculation
  AsoC: amd: add missing snd- module prefix to the acp3x-rn driver kernel module
  ALSA: hda - let hs_mic be picked ahead of hp_mic
  ASoC: rt5682: fix the pop noise while OMTP type headset plugin
  ASoC: fsl_mqs: Fix unchecked return value for clk_prepare_enable
  ASoC: fsl_mqs: Don't check clock is NULL before calling clk API

13 files changed:
include/sound/compress_driver.h
sound/core/compress_offload.c
sound/drivers/opl3/opl3_synth.c
sound/pci/hda/hda_auto_parser.c
sound/pci/hda/patch_hdmi.c
sound/pci/hda/patch_realtek.c
sound/soc/amd/renoir/Makefile
sound/soc/codecs/rt5682.c
sound/soc/fsl/fsl_mqs.c
sound/usb/card.h
sound/usb/endpoint.c
sound/usb/pcm.c
sound/usb/quirks-table.h

index 6ce8effa0b128ffe0464e137bb343f36b9c48874..70cbc5095e7250aeb1fca7f4529516422d45cb2a 100644 (file)
@@ -66,6 +66,7 @@ struct snd_compr_runtime {
  * @direction: stream direction, playback/recording
  * @metadata_set: metadata set flag, true when set
  * @next_track: has userspace signal next track transition, true when set
+ * @partial_drain: undergoing partial_drain for stream, true when set
  * @private_data: pointer to DSP private data
  * @dma_buffer: allocated buffer if any
  */
@@ -78,6 +79,7 @@ struct snd_compr_stream {
        enum snd_compr_direction direction;
        bool metadata_set;
        bool next_track;
+       bool partial_drain;
        void *private_data;
        struct snd_dma_buffer dma_buffer;
 };
@@ -182,7 +184,13 @@ static inline void snd_compr_drain_notify(struct snd_compr_stream *stream)
        if (snd_BUG_ON(!stream))
                return;
 
-       stream->runtime->state = SNDRV_PCM_STATE_SETUP;
+       /* for partial_drain case we are back to running state on success */
+       if (stream->partial_drain) {
+               stream->runtime->state = SNDRV_PCM_STATE_RUNNING;
+               stream->partial_drain = false; /* clear this flag as well */
+       } else {
+               stream->runtime->state = SNDRV_PCM_STATE_SETUP;
+       }
 
        wake_up(&stream->runtime->sleep);
 }
index 509290f2efa8ecee7971c272deba82a4136ba50d..0e53f6f319167c9e4dd3b1e342bf6b8e2fe1f321 100644 (file)
@@ -764,6 +764,9 @@ static int snd_compr_stop(struct snd_compr_stream *stream)
 
        retval = stream->ops->trigger(stream, SNDRV_PCM_TRIGGER_STOP);
        if (!retval) {
+               /* clear flags and stop any drain wait */
+               stream->partial_drain = false;
+               stream->metadata_set = false;
                snd_compr_drain_notify(stream);
                stream->runtime->total_bytes_available = 0;
                stream->runtime->total_bytes_transferred = 0;
@@ -921,6 +924,7 @@ static int snd_compr_partial_drain(struct snd_compr_stream *stream)
        if (stream->next_track == false)
                return -EPERM;
 
+       stream->partial_drain = true;
        retval = stream->ops->trigger(stream, SND_COMPR_TRIGGER_PARTIAL_DRAIN);
        if (retval) {
                pr_debug("Partial drain returned failure\n");
index e69a4ef0d6bdea61218aa9c9b95b06d0cfdcddce..08c10ac9d6c87a75374ac97e787ded3be15c4617 100644 (file)
@@ -91,6 +91,8 @@ int snd_opl3_ioctl(struct snd_hwdep * hw, struct file *file,
                {
                        struct snd_dm_fm_info info;
 
+                       memset(&info, 0, sizeof(info));
+
                        info.fm_mode = opl3->fm_mode;
                        info.rhythm = opl3->rhythm;
                        if (copy_to_user(argp, &info, sizeof(struct snd_dm_fm_info)))
index 2c6d2becfe1a0bce6b1910e9c144d05bb6b1334e..824f4ac1a8ce787d142ad42e17aab63b07b04c1e 100644 (file)
@@ -72,6 +72,12 @@ static int compare_input_type(const void *ap, const void *bp)
        if (a->type != b->type)
                return (int)(a->type - b->type);
 
+       /* If has both hs_mic and hp_mic, pick the hs_mic ahead of hp_mic. */
+       if (a->is_headset_mic && b->is_headphone_mic)
+               return -1; /* don't swap */
+       else if (a->is_headphone_mic && b->is_headset_mic)
+               return 1; /* swap */
+
        /* In case one has boost and the other one has not,
           pick the one with boost first. */
        return (int)(b->has_boost_on_pin - a->has_boost_on_pin);
index e2b21ef5d7d12994be0ca64d332d0d11c24486a0..41eaa89660c3ee9c21ada7e52bd84803129a7f8f 100644 (file)
@@ -259,7 +259,7 @@ static int hinfo_to_pcm_index(struct hda_codec *codec,
                if (get_pcm_rec(spec, pcm_idx)->stream == hinfo)
                        return pcm_idx;
 
-       codec_warn(codec, "HDMI: hinfo %p not registered\n", hinfo);
+       codec_warn(codec, "HDMI: hinfo %p not tied to a PCM\n", hinfo);
        return -EINVAL;
 }
 
@@ -277,7 +277,8 @@ static int hinfo_to_pin_index(struct hda_codec *codec,
                        return pin_idx;
        }
 
-       codec_dbg(codec, "HDMI: hinfo %p not registered\n", hinfo);
+       codec_dbg(codec, "HDMI: hinfo %p (pcm %d) not registered\n", hinfo,
+                 hinfo_to_pcm_index(codec, hinfo));
        return -EINVAL;
 }
 
@@ -1804,33 +1805,43 @@ static int hdmi_add_cvt(struct hda_codec *codec, hda_nid_t cvt_nid)
 
 static int hdmi_parse_codec(struct hda_codec *codec)
 {
-       hda_nid_t nid;
+       hda_nid_t start_nid;
+       unsigned int caps;
        int i, nodes;
 
-       nodes = snd_hda_get_sub_nodes(codec, codec->core.afg, &nid);
-       if (!nid || nodes < 0) {
+       nodes = snd_hda_get_sub_nodes(codec, codec->core.afg, &start_nid);
+       if (!start_nid || nodes < 0) {
                codec_warn(codec, "HDMI: failed to get afg sub nodes\n");
                return -EINVAL;
        }
 
-       for (i = 0; i < nodes; i++, nid++) {
-               unsigned int caps;
-               unsigned int type;
+       /*
+        * hdmi_add_pin() assumes total amount of converters to
+        * be known, so first discover all converters
+        */
+       for (i = 0; i < nodes; i++) {
+               hda_nid_t nid = start_nid + i;
 
                caps = get_wcaps(codec, nid);
-               type = get_wcaps_type(caps);
 
                if (!(caps & AC_WCAP_DIGITAL))
                        continue;
 
-               switch (type) {
-               case AC_WID_AUD_OUT:
+               if (get_wcaps_type(caps) == AC_WID_AUD_OUT)
                        hdmi_add_cvt(codec, nid);
-                       break;
-               case AC_WID_PIN:
+       }
+
+       /* discover audio pins */
+       for (i = 0; i < nodes; i++) {
+               hda_nid_t nid = start_nid + i;
+
+               caps = get_wcaps(codec, nid);
+
+               if (!(caps & AC_WCAP_DIGITAL))
+                       continue;
+
+               if (get_wcaps_type(caps) == AC_WID_PIN)
                        hdmi_add_pin(codec, nid);
-                       break;
-               }
        }
 
        return 0;
index 737ef82a75fda2a6a8aecf7238df648c3fc6c1e9..194ffa8c66cedb9890d98073e14794423ed5ba18 100644 (file)
@@ -6149,6 +6149,9 @@ enum {
        ALC236_FIXUP_HP_MUTE_LED,
        ALC298_FIXUP_SAMSUNG_HEADPHONE_VERY_QUIET,
        ALC295_FIXUP_ASUS_MIC_NO_PRESENCE,
+       ALC269VC_FIXUP_ACER_VCOPPERBOX_PINS,
+       ALC269VC_FIXUP_ACER_HEADSET_MIC,
+       ALC269VC_FIXUP_ACER_MIC_NO_PRESENCE,
 };
 
 static const struct hda_fixup alc269_fixups[] = {
@@ -7327,6 +7330,35 @@ static const struct hda_fixup alc269_fixups[] = {
                .chained = true,
                .chain_id = ALC269_FIXUP_HEADSET_MODE
        },
+       [ALC269VC_FIXUP_ACER_VCOPPERBOX_PINS] = {
+               .type = HDA_FIXUP_PINS,
+               .v.pins = (const struct hda_pintbl[]) {
+                       { 0x14, 0x90100120 }, /* use as internal speaker */
+                       { 0x18, 0x02a111f0 }, /* use as headset mic, without its own jack detect */
+                       { 0x1a, 0x01011020 }, /* use as line out */
+                       { },
+               },
+               .chained = true,
+               .chain_id = ALC269_FIXUP_HEADSET_MIC
+       },
+       [ALC269VC_FIXUP_ACER_HEADSET_MIC] = {
+               .type = HDA_FIXUP_PINS,
+               .v.pins = (const struct hda_pintbl[]) {
+                       { 0x18, 0x02a11030 }, /* use as headset mic */
+                       { }
+               },
+               .chained = true,
+               .chain_id = ALC269_FIXUP_HEADSET_MIC
+       },
+       [ALC269VC_FIXUP_ACER_MIC_NO_PRESENCE] = {
+               .type = HDA_FIXUP_PINS,
+               .v.pins = (const struct hda_pintbl[]) {
+                       { 0x18, 0x01a11130 }, /* use as headset mic, without its own jack detect */
+                       { }
+               },
+               .chained = true,
+               .chain_id = ALC269_FIXUP_HEADSET_MIC
+       },
 };
 
 static const struct snd_pci_quirk alc269_fixup_tbl[] = {
@@ -7342,10 +7374,13 @@ static const struct snd_pci_quirk alc269_fixup_tbl[] = {
        SND_PCI_QUIRK(0x1025, 0x0775, "Acer Aspire E1-572", ALC271_FIXUP_HP_GATE_MIC_JACK_E1_572),
        SND_PCI_QUIRK(0x1025, 0x079b, "Acer Aspire V5-573G", ALC282_FIXUP_ASPIRE_V5_PINS),
        SND_PCI_QUIRK(0x1025, 0x102b, "Acer Aspire C24-860", ALC286_FIXUP_ACER_AIO_MIC_NO_PRESENCE),
+       SND_PCI_QUIRK(0x1025, 0x1065, "Acer Aspire C20-820", ALC269VC_FIXUP_ACER_HEADSET_MIC),
        SND_PCI_QUIRK(0x1025, 0x106d, "Acer Cloudbook 14", ALC283_FIXUP_CHROME_BOOK),
        SND_PCI_QUIRK(0x1025, 0x1099, "Acer Aspire E5-523G", ALC255_FIXUP_ACER_MIC_NO_PRESENCE),
        SND_PCI_QUIRK(0x1025, 0x110e, "Acer Aspire ES1-432", ALC255_FIXUP_ACER_MIC_NO_PRESENCE),
        SND_PCI_QUIRK(0x1025, 0x1246, "Acer Predator Helios 500", ALC299_FIXUP_PREDATOR_SPK),
+       SND_PCI_QUIRK(0x1025, 0x1247, "Acer vCopperbox", ALC269VC_FIXUP_ACER_VCOPPERBOX_PINS),
+       SND_PCI_QUIRK(0x1025, 0x1248, "Acer Veriton N4660G", ALC269VC_FIXUP_ACER_MIC_NO_PRESENCE),
        SND_PCI_QUIRK(0x1025, 0x128f, "Acer Veriton Z6860G", ALC286_FIXUP_ACER_AIO_HEADSET_MIC),
        SND_PCI_QUIRK(0x1025, 0x1290, "Acer Veriton Z4860G", ALC286_FIXUP_ACER_AIO_HEADSET_MIC),
        SND_PCI_QUIRK(0x1025, 0x1291, "Acer Veriton Z4660G", ALC286_FIXUP_ACER_AIO_HEADSET_MIC),
@@ -7571,8 +7606,7 @@ static const struct snd_pci_quirk alc269_fixup_tbl[] = {
        SND_PCI_QUIRK(0x17aa, 0x224c, "Thinkpad", ALC298_FIXUP_TPT470_DOCK),
        SND_PCI_QUIRK(0x17aa, 0x224d, "Thinkpad", ALC298_FIXUP_TPT470_DOCK),
        SND_PCI_QUIRK(0x17aa, 0x225d, "Thinkpad T480", ALC269_FIXUP_LIMIT_INT_MIC_BOOST),
-       SND_PCI_QUIRK(0x17aa, 0x2292, "Thinkpad X1 Yoga 7th", ALC285_FIXUP_THINKPAD_HEADSET_JACK),
-       SND_PCI_QUIRK(0x17aa, 0x2293, "Thinkpad X1 Carbon 7th", ALC285_FIXUP_THINKPAD_HEADSET_JACK),
+       SND_PCI_QUIRK(0x17aa, 0x2292, "Thinkpad X1 Carbon 7th", ALC285_FIXUP_THINKPAD_HEADSET_JACK),
        SND_PCI_QUIRK(0x17aa, 0x22be, "Thinkpad X1 Carbon 8th", ALC285_FIXUP_THINKPAD_HEADSET_JACK),
        SND_PCI_QUIRK(0x17aa, 0x30bb, "ThinkCentre AIO", ALC233_FIXUP_LENOVO_LINE2_MIC_HOTKEY),
        SND_PCI_QUIRK(0x17aa, 0x30e2, "ThinkCentre AIO", ALC233_FIXUP_LENOVO_LINE2_MIC_HOTKEY),
index e4371932a55a66a6215b6478646570a8dcb1131e..4a82690aec16a91207038bd495d449ae2a37a254 100644 (file)
@@ -2,6 +2,7 @@
 # Renoir platform Support
 snd-rn-pci-acp3x-objs  := rn-pci-acp3x.o
 snd-acp3x-pdm-dma-objs := acp3x-pdm-dma.o
-obj-$(CONFIG_SND_SOC_AMD_RENOIR)        += snd-rn-pci-acp3x.o
-obj-$(CONFIG_SND_SOC_AMD_RENOIR)        += snd-acp3x-pdm-dma.o
-obj-$(CONFIG_SND_SOC_AMD_RENOIR_MACH)  += acp3x-rn.o
+snd-acp3x-rn-objs      := acp3x-rn.o
+obj-$(CONFIG_SND_SOC_AMD_RENOIR)       += snd-rn-pci-acp3x.o
+obj-$(CONFIG_SND_SOC_AMD_RENOIR)       += snd-acp3x-pdm-dma.o
+obj-$(CONFIG_SND_SOC_AMD_RENOIR_MACH)  += snd-acp3x-rn.o
index 3e9d2c6c51f9a60cacab882ddcf91f0fa2601ddc..7d6670abdb08e0fa9903b3ac08ae0790002bb2a8 100644 (file)
@@ -932,7 +932,9 @@ int rt5682_headset_detect(struct snd_soc_component *component, int jack_insert)
                        RT5682_PWR_ANLG_1, RT5682_PWR_FV2, RT5682_PWR_FV2);
                snd_soc_component_update_bits(component, RT5682_PWR_ANLG_3,
                        RT5682_PWR_CBJ, RT5682_PWR_CBJ);
-
+               snd_soc_component_update_bits(component,
+                       RT5682_HP_CHARGE_PUMP_1,
+                       RT5682_OSW_L_MASK | RT5682_OSW_R_MASK, 0);
                snd_soc_component_update_bits(component, RT5682_CBJ_CTRL_1,
                        RT5682_TRIG_JD_MASK, RT5682_TRIG_JD_HIGH);
 
@@ -956,6 +958,11 @@ int rt5682_headset_detect(struct snd_soc_component *component, int jack_insert)
                        rt5682->jack_type = SND_JACK_HEADPHONE;
                        break;
                }
+
+               snd_soc_component_update_bits(component,
+                       RT5682_HP_CHARGE_PUMP_1,
+                       RT5682_OSW_L_MASK | RT5682_OSW_R_MASK,
+                       RT5682_OSW_L_EN | RT5682_OSW_R_EN);
        } else {
                rt5682_enable_push_button_irq(component, false);
                snd_soc_component_update_bits(component, RT5682_CBJ_CTRL_1,
index 0c813a45bba7c1e17144e1a54fc9c731d07d2e5c..69aeb0e71844d9f614e156f4e8a0cbcdb41d728f 100644 (file)
@@ -265,12 +265,20 @@ static int fsl_mqs_remove(struct platform_device *pdev)
 static int fsl_mqs_runtime_resume(struct device *dev)
 {
        struct fsl_mqs *mqs_priv = dev_get_drvdata(dev);
+       int ret;
 
-       if (mqs_priv->ipg)
-               clk_prepare_enable(mqs_priv->ipg);
+       ret = clk_prepare_enable(mqs_priv->ipg);
+       if (ret) {
+               dev_err(dev, "failed to enable ipg clock\n");
+               return ret;
+       }
 
-       if (mqs_priv->mclk)
-               clk_prepare_enable(mqs_priv->mclk);
+       ret = clk_prepare_enable(mqs_priv->mclk);
+       if (ret) {
+               dev_err(dev, "failed to enable mclk clock\n");
+               clk_disable_unprepare(mqs_priv->ipg);
+               return ret;
+       }
 
        if (mqs_priv->use_gpr)
                regmap_write(mqs_priv->regmap, IOMUXC_GPR2,
@@ -292,11 +300,8 @@ static int fsl_mqs_runtime_suspend(struct device *dev)
                regmap_read(mqs_priv->regmap, REG_MQS_CTRL,
                            &mqs_priv->reg_mqs_ctrl);
 
-       if (mqs_priv->mclk)
-               clk_disable_unprepare(mqs_priv->mclk);
-
-       if (mqs_priv->ipg)
-               clk_disable_unprepare(mqs_priv->ipg);
+       clk_disable_unprepare(mqs_priv->mclk);
+       clk_disable_unprepare(mqs_priv->ipg);
 
        return 0;
 }
index d6219fba96995bf187284a813546c005340b4a70..de43267b9c8af77dca423d8a1bc6079323806ac9 100644 (file)
@@ -84,10 +84,10 @@ struct snd_usb_endpoint {
        dma_addr_t sync_dma;            /* DMA address of syncbuf */
 
        unsigned int pipe;              /* the data i/o pipe */
-       unsigned int framesize[2];      /* small/large frame sizes in samples */
-       unsigned int sample_rem;        /* remainder from division fs/fps */
+       unsigned int packsize[2];       /* small/large packet sizes in samples */
+       unsigned int sample_rem;        /* remainder from division fs/pps */
        unsigned int sample_accum;      /* sample accumulator */
-       unsigned int fps;               /* frames per second */
+       unsigned int pps;               /* packets per second */
        unsigned int freqn;             /* nominal sampling rate in fs/fps in Q16.16 format */
        unsigned int freqm;             /* momentary sampling rate in fs/fps in Q16.16 format */
        int        freqshift;           /* how much to shift the feedback value to get Q16.16 */
index 9bea7d3f99f88f169806aa9f11bdffe9daaa1a78..88760268fb5568eddbb7cdf90e7961664f31b717 100644 (file)
@@ -159,11 +159,11 @@ int snd_usb_endpoint_next_packet_size(struct snd_usb_endpoint *ep)
                return ep->maxframesize;
 
        ep->sample_accum += ep->sample_rem;
-       if (ep->sample_accum >= ep->fps) {
-               ep->sample_accum -= ep->fps;
-               ret = ep->framesize[1];
+       if (ep->sample_accum >= ep->pps) {
+               ep->sample_accum -= ep->pps;
+               ret = ep->packsize[1];
        } else {
-               ret = ep->framesize[0];
+               ret = ep->packsize[0];
        }
 
        return ret;
@@ -1088,15 +1088,15 @@ int snd_usb_endpoint_set_params(struct snd_usb_endpoint *ep,
 
        if (snd_usb_get_speed(ep->chip->dev) == USB_SPEED_FULL) {
                ep->freqn = get_usb_full_speed_rate(rate);
-               ep->fps = 1000;
+               ep->pps = 1000 >> ep->datainterval;
        } else {
                ep->freqn = get_usb_high_speed_rate(rate);
-               ep->fps = 8000;
+               ep->pps = 8000 >> ep->datainterval;
        }
 
-       ep->sample_rem = rate % ep->fps;
-       ep->framesize[0] = rate / ep->fps;
-       ep->framesize[1] = (rate + (ep->fps - 1)) / ep->fps;
+       ep->sample_rem = rate % ep->pps;
+       ep->packsize[0] = rate / ep->pps;
+       ep->packsize[1] = (rate + (ep->pps - 1)) / ep->pps;
 
        /* calculate the frequency in 16.16 format */
        ep->freqm = ep->freqn;
index a777d36c4f5a87d88e07cf0f151784d472c875b0..40b7cd13fed9c9e67462456ac4bbbb26d4ba6c55 100644 (file)
@@ -368,6 +368,7 @@ static int set_sync_ep_implicit_fb_quirk(struct snd_usb_substream *subs,
                goto add_sync_ep_from_ifnum;
        case USB_ID(0x07fd, 0x0008): /* MOTU M Series */
        case USB_ID(0x31e9, 0x0002): /* Solid State Logic SSL2+ */
+       case USB_ID(0x0d9a, 0x00df): /* RTX6001 */
                ep = 0x81;
                ifnum = 2;
                goto add_sync_ep_from_ifnum;
index 4ec491011b19c2ba7196226dd2240a25eac065ce..9092cc0aa8072b4fc7a9a7ddf24a2917a022def2 100644 (file)
@@ -3633,4 +3633,56 @@ ALC1220_VB_DESKTOP(0x26ce, 0x0a01), /* Asrock TRX40 Creator */
        }
 },
 
+/*
+ * MacroSilicon MS2109 based HDMI capture cards
+ *
+ * These claim 96kHz 1ch in the descriptors, but are actually 48kHz 2ch.
+ * They also need QUIRK_AUDIO_ALIGN_TRANSFER, which makes one wonder if
+ * they pretend to be 96kHz mono as a workaround for stereo being broken
+ * by that...
+ *
+ * They also have swapped L-R channels, but that's for userspace to deal
+ * with.
+ */
+{
+       USB_DEVICE(0x534d, 0x2109),
+       .driver_info = (unsigned long) &(const struct snd_usb_audio_quirk) {
+               .vendor_name = "MacroSilicon",
+               .product_name = "MS2109",
+               .ifnum = QUIRK_ANY_INTERFACE,
+               .type = QUIRK_COMPOSITE,
+               .data = &(const struct snd_usb_audio_quirk[]) {
+                       {
+                               .ifnum = 2,
+                               .type = QUIRK_AUDIO_ALIGN_TRANSFER,
+                       },
+                       {
+                               .ifnum = 2,
+                               .type = QUIRK_AUDIO_STANDARD_MIXER,
+                       },
+                       {
+                               .ifnum = 3,
+                               .type = QUIRK_AUDIO_FIXED_ENDPOINT,
+                               .data = &(const struct audioformat) {
+                                       .formats = SNDRV_PCM_FMTBIT_S16_LE,
+                                       .channels = 2,
+                                       .iface = 3,
+                                       .altsetting = 1,
+                                       .altset_idx = 1,
+                                       .attributes = 0,
+                                       .endpoint = 0x82,
+                                       .ep_attr = USB_ENDPOINT_XFER_ISOC |
+                                               USB_ENDPOINT_SYNC_ASYNC,
+                                       .rates = SNDRV_PCM_RATE_CONTINUOUS,
+                                       .rate_min = 48000,
+                                       .rate_max = 48000,
+                               }
+                       },
+                       {
+                               .ifnum = -1
+                       }
+               }
+       }
+},
+
 #undef USB_DEVICE_VENDOR_SPEC