Merge tag 'asoc-v5.17' of https://git.kernel.org/pub/scm/linux/kernel/git/broonie...
authorTakashi Iwai <tiwai@suse.de>
Wed, 5 Jan 2022 14:39:24 +0000 (15:39 +0100)
committerTakashi Iwai <tiwai@suse.de>
Wed, 5 Jan 2022 14:39:24 +0000 (15:39 +0100)
ASoC: Updates for v5.17

Not much going on framework release this time, but a big update for
drivers especially the Intel and SOF ones.

 - Refinements and cleanups around the delay() APIs.
 - Wider use of dev_err_probe().
 - Continuing cleanups and improvements to the SOF code.
 - Support for pin switches in simple-card derived cards.
 - Support for AMD Renoir ACP, Asahi Kasei Microdevices AKM4375, Intel
   systems using NAU8825 and MAX98390, Mediatek MT8915, nVidia Tegra20
   S/PDIF, Qualcomm systems using ALC5682I-VS and Texas Instruments
   TLV320ADC3xxx.

78 files changed:
Documentation/devicetree/bindings/sound/nvidia,tegra30-hda.yaml
Documentation/sound/hd-audio/models.rst
MAINTAINERS
arch/arm64/boot/dts/nvidia/tegra194.dtsi
include/sound/hda_codec.h
include/sound/hdaudio.h
include/sound/hdaudio_ext.h
include/sound/intel-nhlt.h
include/sound/memalloc.h
include/sound/pcm.h
include/uapi/sound/asound.h
sound/core/Makefile
sound/core/control_compat.c
sound/core/control_led.c
sound/core/info_oss.c
sound/core/jack.c
sound/core/memalloc.c
sound/core/oss/pcm_oss.c
sound/core/pcm.c
sound/core/rawmidi.c
sound/core/seq/seq_queue.c
sound/core/sgbuf.c [deleted file]
sound/drivers/opl3/opl3_midi.c
sound/hda/ext/hdac_ext_stream.c
sound/hda/hdac_stream.c
sound/hda/intel-dsp-config.c
sound/hda/intel-nhlt.c
sound/hda/intel-sdw-acpi.c
sound/isa/gus/gus_mem.c
sound/pci/ac97/ac97_pcm.c
sound/pci/cmipci.c
sound/pci/ctxfi/ctamixer.c
sound/pci/ctxfi/ctdaio.c
sound/pci/ctxfi/ctresource.c
sound/pci/ctxfi/ctresource.h
sound/pci/ctxfi/ctsrc.c
sound/pci/hda/hda_auto_parser.c
sound/pci/hda/hda_bind.c
sound/pci/hda/hda_codec.c
sound/pci/hda/hda_controller.c
sound/pci/hda/hda_generic.h
sound/pci/hda/hda_intel.c
sound/pci/hda/hda_jack.c
sound/pci/hda/hda_jack.h
sound/pci/hda/hda_local.h
sound/pci/hda/hda_tegra.c
sound/pci/hda/patch_cs8409-tables.c
sound/pci/hda/patch_cs8409.c
sound/pci/hda/patch_cs8409.h
sound/pci/hda/patch_hdmi.c
sound/pci/hda/patch_realtek.c
sound/pci/mixart/mixart_core.c
sound/pci/mixart/mixart_core.h
sound/ppc/beep.c
sound/soc/codecs/sta350.h
sound/soc/codecs/tlv320aic26.h
sound/soc/intel/skylake/skl-nhlt.c
sound/soc/intel/skylake/skl-pcm.c
sound/soc/intel/skylake/skl-topology.c
sound/soc/intel/skylake/skl-topology.h
sound/soc/intel/skylake/skl.c
sound/soc/intel/skylake/skl.h
sound/sparc/dbri.c
sound/usb/card.c
sound/usb/format.c
sound/usb/mixer.c
sound/usb/mixer.h
sound/usb/mixer_maps.c
sound/usb/mixer_quirks.c
sound/usb/mixer_quirks.h
sound/usb/pcm.c
sound/usb/power.h
sound/usb/quirks.c
sound/usb/usx2y/usbusx2y.c
tools/testing/selftests/Makefile
tools/testing/selftests/alsa/.gitignore [new file with mode: 0644]
tools/testing/selftests/alsa/Makefile [new file with mode: 0644]
tools/testing/selftests/alsa/mixer-test.c [new file with mode: 0644]

index b55775e..2c913aa 100644 (file)
@@ -50,9 +50,11 @@ properties:
       - const: hda2codec_2x
 
   resets:
+    minItems: 2
     maxItems: 3
 
   reset-names:
+    minItems: 2
     items:
       - const: hda
       - const: hda2hdmi
index 0ea967d..d253359 100644 (file)
@@ -326,6 +326,8 @@ usi-headset
     Headset support on USI machines
 dual-codecs
     Lenovo laptops with dual codecs
+alc285-hp-amp-init
+    HP laptops which require speaker amplifier initialization (ALC285)
 
 ALC680
 ======
index 0973425..74d7d20 100644 (file)
@@ -17806,6 +17806,7 @@ F:      Documentation/sound/
 F:     include/sound/
 F:     include/uapi/sound/
 F:     sound/
+F:     tools/testing/selftests/alsa
 
 SOUND - COMPRESSED AUDIO
 M:     Vinod Koul <vkoul@kernel.org>
@@ -17825,6 +17826,13 @@ F:     include/sound/dmaengine_pcm.h
 F:     sound/core/pcm_dmaengine.c
 F:     sound/soc/soc-generic-dmaengine-pcm.c
 
+SOUND - ALSA SELFTESTS
+M:     Mark Brown <broonie@kernel.org>
+L:     alsa-devel@alsa-project.org (moderated for non-subscribers)
+L:     linux-kselftest@vger.kernel.org
+S:     Supported
+F:     tools/testing/selftests/alsa
+
 SOUND - SOC LAYER / DYNAMIC AUDIO POWER MANAGEMENT (ASoC)
 M:     Liam Girdwood <lgirdwood@gmail.com>
 M:     Mark Brown <broonie@kernel.org>
index 851e049..dcc0e55 100644 (file)
                                 <&bpmp TEGRA194_CLK_HDA2CODEC_2X>;
                        clock-names = "hda", "hda2hdmi", "hda2codec_2x";
                        resets = <&bpmp TEGRA194_RESET_HDA>,
-                                <&bpmp TEGRA194_RESET_HDA2HDMICODEC>,
-                                <&bpmp TEGRA194_RESET_HDA2CODEC_2X>;
-                       reset-names = "hda", "hda2hdmi", "hda2codec_2x";
+                                <&bpmp TEGRA194_RESET_HDA2HDMICODEC>;
+                       reset-names = "hda", "hda2hdmi";
                        power-domains = <&bpmp TEGRA194_POWER_DOMAIN_DISP>;
                        interconnects = <&mc TEGRA194_MEMORY_CLIENT_HDAR &emc>,
                                        <&mc TEGRA194_MEMORY_CLIENT_HDAW &emc>;
index 0e45963..82d9daa 100644 (file)
@@ -8,7 +8,7 @@
 #ifndef __SOUND_HDA_CODEC_H
 #define __SOUND_HDA_CODEC_H
 
-#include <linux/kref.h>
+#include <linux/refcount.h>
 #include <linux/mod_devicetable.h>
 #include <sound/info.h>
 #include <sound/control.h>
@@ -166,8 +166,8 @@ struct hda_pcm {
        bool own_chmap;         /* codec driver provides own channel maps */
        /* private: */
        struct hda_codec *codec;
-       struct kref kref;
        struct list_head list;
+       unsigned int disconnected:1;
 };
 
 /* codec information */
@@ -187,6 +187,8 @@ struct hda_codec {
 
        /* PCM to create, set by patch_ops.build_pcms callback */
        struct list_head pcm_list_head;
+       refcount_t pcm_ref;
+       wait_queue_head_t remove_sleep;
 
        /* codec specific info */
        void *spec;
@@ -420,7 +422,7 @@ void snd_hda_codec_cleanup_for_unbind(struct hda_codec *codec);
 
 static inline void snd_hda_codec_pcm_get(struct hda_pcm *pcm)
 {
-       kref_get(&pcm->kref);
+       refcount_inc(&pcm->codec->pcm_ref);
 }
 void snd_hda_codec_pcm_put(struct hda_pcm *pcm);
 
index 22af68b..6a90ce4 100644 (file)
@@ -558,6 +558,7 @@ int snd_hdac_stream_set_params(struct hdac_stream *azx_dev,
 void snd_hdac_stream_start(struct hdac_stream *azx_dev, bool fresh_start);
 void snd_hdac_stream_clear(struct hdac_stream *azx_dev);
 void snd_hdac_stream_stop(struct hdac_stream *azx_dev);
+void snd_hdac_stop_streams_and_chip(struct hdac_bus *bus);
 void snd_hdac_stream_reset(struct hdac_stream *azx_dev);
 void snd_hdac_stream_sync_trigger(struct hdac_stream *azx_dev, bool set,
                                  unsigned int streams, unsigned int reg);
index d4e31ea..77123c3 100644 (file)
@@ -78,36 +78,35 @@ struct hdac_ext_stream {
        container_of(s, struct hdac_ext_stream, hstream)
 
 void snd_hdac_ext_stream_init(struct hdac_bus *bus,
-                               struct hdac_ext_stream *stream, int idx,
-                               int direction, int tag);
+                             struct hdac_ext_stream *hext_stream, int idx,
+                             int direction, int tag);
 int snd_hdac_ext_stream_init_all(struct hdac_bus *bus, int start_idx,
-               int num_stream, int dir);
+                                int num_stream, int dir);
 void snd_hdac_stream_free_all(struct hdac_bus *bus);
 void snd_hdac_link_free_all(struct hdac_bus *bus);
 struct hdac_ext_stream *snd_hdac_ext_stream_assign(struct hdac_bus *bus,
                                           struct snd_pcm_substream *substream,
                                           int type);
-void snd_hdac_ext_stream_release(struct hdac_ext_stream *azx_dev, int type);
+void snd_hdac_ext_stream_release(struct hdac_ext_stream *hext_stream, int type);
 void snd_hdac_ext_stream_decouple_locked(struct hdac_bus *bus,
-                                 struct hdac_ext_stream *azx_dev, bool decouple);
+                                        struct hdac_ext_stream *hext_stream, bool decouple);
 void snd_hdac_ext_stream_decouple(struct hdac_bus *bus,
                                struct hdac_ext_stream *azx_dev, bool decouple);
-void snd_hdac_ext_stop_streams(struct hdac_bus *bus);
 
 int snd_hdac_ext_stream_set_spib(struct hdac_bus *bus,
-                                struct hdac_ext_stream *stream, u32 value);
+                                struct hdac_ext_stream *hext_stream, u32 value);
 int snd_hdac_ext_stream_get_spbmaxfifo(struct hdac_bus *bus,
-                                struct hdac_ext_stream *stream);
+                                      struct hdac_ext_stream *hext_stream);
 void snd_hdac_ext_stream_drsm_enable(struct hdac_bus *bus,
                                bool enable, int index);
 int snd_hdac_ext_stream_set_dpibr(struct hdac_bus *bus,
-                               struct hdac_ext_stream *stream, u32 value);
-int snd_hdac_ext_stream_set_lpib(struct hdac_ext_stream *stream, u32 value);
+                               struct hdac_ext_stream *hext_stream, u32 value);
+int snd_hdac_ext_stream_set_lpib(struct hdac_ext_stream *hext_stream, u32 value);
 
-void snd_hdac_ext_link_stream_start(struct hdac_ext_stream *hstream);
-void snd_hdac_ext_link_stream_clear(struct hdac_ext_stream *hstream);
-void snd_hdac_ext_link_stream_reset(struct hdac_ext_stream *hstream);
-int snd_hdac_ext_link_stream_setup(struct hdac_ext_stream *stream, int fmt);
+void snd_hdac_ext_link_stream_start(struct hdac_ext_stream *hext_stream);
+void snd_hdac_ext_link_stream_clear(struct hdac_ext_stream *hext_stream);
+void snd_hdac_ext_link_stream_reset(struct hdac_ext_stream *hext_stream);
+int snd_hdac_ext_link_stream_setup(struct hdac_ext_stream *hext_stream, int fmt);
 
 struct hdac_ext_link {
        struct hdac_bus *bus;
index d057480..089a760 100644 (file)
 
 #include <linux/acpi.h>
 
+enum nhlt_link_type {
+       NHLT_LINK_HDA = 0,
+       NHLT_LINK_DSP = 1,
+       NHLT_LINK_DMIC = 2,
+       NHLT_LINK_SSP = 3,
+       NHLT_LINK_INVALID
+};
+
 #if IS_ENABLED(CONFIG_ACPI) && IS_ENABLED(CONFIG_SND_INTEL_NHLT)
 
 struct wav_fmt {
@@ -33,14 +41,6 @@ struct wav_fmt_ext {
        u8 sub_fmt[16];
 } __packed;
 
-enum nhlt_link_type {
-       NHLT_LINK_HDA = 0,
-       NHLT_LINK_DSP = 1,
-       NHLT_LINK_DMIC = 2,
-       NHLT_LINK_SSP = 3,
-       NHLT_LINK_INVALID
-};
-
 enum nhlt_device_type {
        NHLT_DEVICE_BT = 0,
        NHLT_DEVICE_DMIC = 1,
@@ -132,6 +132,12 @@ void intel_nhlt_free(struct nhlt_acpi_table *addr);
 
 int intel_nhlt_get_dmic_geo(struct device *dev, struct nhlt_acpi_table *nhlt);
 
+bool intel_nhlt_has_endpoint_type(struct nhlt_acpi_table *nhlt, u8 link_type);
+struct nhlt_specific_cfg *
+intel_nhlt_get_endpoint_blob(struct device *dev, struct nhlt_acpi_table *nhlt,
+                            u32 bus_id, u8 link_type, u8 vbps, u8 bps,
+                            u8 num_ch, u32 rate, u8 dir, u8 dev_type);
+
 #else
 
 struct nhlt_acpi_table;
@@ -150,6 +156,21 @@ static inline int intel_nhlt_get_dmic_geo(struct device *dev,
 {
        return 0;
 }
+
+static inline bool intel_nhlt_has_endpoint_type(struct nhlt_acpi_table *nhlt,
+                                               u8 link_type)
+{
+       return false;
+}
+
+static inline struct nhlt_specific_cfg *
+intel_nhlt_get_endpoint_blob(struct device *dev, struct nhlt_acpi_table *nhlt,
+                            u32 bus_id, u8 link_type, u8 vbps, u8 bps,
+                            u8 num_ch, u32 rate, u8 dir, u8 dev_type)
+{
+       return NULL;
+}
+
 #endif
 
 #endif
index 1051b84..653dfff 100644 (file)
@@ -36,13 +36,6 @@ struct snd_dma_device {
 #define SNDRV_DMA_TYPE_CONTINUOUS      1       /* continuous no-DMA memory */
 #define SNDRV_DMA_TYPE_DEV             2       /* generic device continuous */
 #define SNDRV_DMA_TYPE_DEV_WC          5       /* continuous write-combined */
-#ifdef CONFIG_SND_DMA_SGBUF
-#define SNDRV_DMA_TYPE_DEV_SG          3       /* generic device SG-buffer */
-#define SNDRV_DMA_TYPE_DEV_WC_SG       6       /* SG write-combined */
-#else
-#define SNDRV_DMA_TYPE_DEV_SG  SNDRV_DMA_TYPE_DEV /* no SG-buf support */
-#define SNDRV_DMA_TYPE_DEV_WC_SG       SNDRV_DMA_TYPE_DEV_WC
-#endif
 #ifdef CONFIG_GENERIC_ALLOCATOR
 #define SNDRV_DMA_TYPE_DEV_IRAM                4       /* generic device iram-buffer */
 #else
@@ -51,6 +44,13 @@ struct snd_dma_device {
 #define SNDRV_DMA_TYPE_VMALLOC         7       /* vmalloc'ed buffer */
 #define SNDRV_DMA_TYPE_NONCONTIG       8       /* non-coherent SG buffer */
 #define SNDRV_DMA_TYPE_NONCOHERENT     9       /* non-coherent buffer */
+#ifdef CONFIG_SND_DMA_SGBUF
+#define SNDRV_DMA_TYPE_DEV_SG          SNDRV_DMA_TYPE_NONCONTIG
+#define SNDRV_DMA_TYPE_DEV_WC_SG       6       /* SG write-combined */
+#else
+#define SNDRV_DMA_TYPE_DEV_SG  SNDRV_DMA_TYPE_DEV /* no SG-buf support */
+#define SNDRV_DMA_TYPE_DEV_WC_SG       SNDRV_DMA_TYPE_DEV_WC
+#endif
 
 /*
  * info for buffer allocation
index 33451f8..9b187d8 100644 (file)
@@ -147,6 +147,9 @@ struct snd_pcm_ops {
 #define SNDRV_PCM_FMTBIT_S24_BE                _SNDRV_PCM_FMTBIT(S24_BE)
 #define SNDRV_PCM_FMTBIT_U24_LE                _SNDRV_PCM_FMTBIT(U24_LE)
 #define SNDRV_PCM_FMTBIT_U24_BE                _SNDRV_PCM_FMTBIT(U24_BE)
+// For S32/U32 formats, 'msbits' hardware parameter is often used to deliver information about the
+// available bit count in most significant bit. It's for the case of so-called 'left-justified' or
+// `right-padding` sample which has less width than 32 bit.
 #define SNDRV_PCM_FMTBIT_S32_LE                _SNDRV_PCM_FMTBIT(S32_LE)
 #define SNDRV_PCM_FMTBIT_S32_BE                _SNDRV_PCM_FMTBIT(S32_BE)
 #define SNDRV_PCM_FMTBIT_U32_LE                _SNDRV_PCM_FMTBIT(U32_LE)
index ff7e638..ef0cafe 100644 (file)
@@ -202,6 +202,11 @@ typedef int __bitwise snd_pcm_format_t;
 #define        SNDRV_PCM_FORMAT_S24_BE ((__force snd_pcm_format_t) 7) /* low three bytes */
 #define        SNDRV_PCM_FORMAT_U24_LE ((__force snd_pcm_format_t) 8) /* low three bytes */
 #define        SNDRV_PCM_FORMAT_U24_BE ((__force snd_pcm_format_t) 9) /* low three bytes */
+/*
+ * For S32/U32 formats, 'msbits' hardware parameter is often used to deliver information about the
+ * available bit count in most significant bit. It's for the case of so-called 'left-justified' or
+ * `right-padding` sample which has less width than 32 bit.
+ */
 #define        SNDRV_PCM_FORMAT_S32_LE ((__force snd_pcm_format_t) 10)
 #define        SNDRV_PCM_FORMAT_S32_BE ((__force snd_pcm_format_t) 11)
 #define        SNDRV_PCM_FORMAT_U32_LE ((__force snd_pcm_format_t) 12)
index 79e1407..350d704 100644 (file)
@@ -19,7 +19,6 @@ snd-$(CONFIG_SND_JACK)          += ctljack.o jack.o
 snd-pcm-y := pcm.o pcm_native.o pcm_lib.o pcm_misc.o \
                pcm_memory.o memalloc.o
 snd-pcm-$(CONFIG_SND_PCM_TIMER) += pcm_timer.o
-snd-pcm-$(CONFIG_SND_DMA_SGBUF) += sgbuf.o
 snd-pcm-$(CONFIG_SND_PCM_ELD) += pcm_drm_eld.o
 snd-pcm-$(CONFIG_SND_PCM_IEC958) += pcm_iec958.o
 
index 470dabc..edff063 100644 (file)
@@ -264,6 +264,7 @@ static int copy_ctl_value_to_user(void __user *userdata,
                                  struct snd_ctl_elem_value *data,
                                  int type, int count)
 {
+       struct snd_ctl_elem_value32 __user *data32 = userdata;
        int i, size;
 
        if (type == SNDRV_CTL_ELEM_TYPE_BOOLEAN ||
@@ -280,6 +281,8 @@ static int copy_ctl_value_to_user(void __user *userdata,
                if (copy_to_user(valuep, data->value.bytes.data, size))
                        return -EFAULT;
        }
+       if (copy_to_user(&data32->id, &data->id, sizeof(data32->id)))
+               return -EFAULT;
        return 0;
 }
 
index a95332b..207828f 100644 (file)
@@ -509,7 +509,7 @@ static char *parse_string(char *s, char *val, size_t val_size)
        return s;
 }
 
-static char *parse_iface(char *s, unsigned int *val)
+static char *parse_iface(char *s, snd_ctl_elem_iface_t *val)
 {
        if (!strncasecmp(s, "card", 4))
                *val = SNDRV_CTL_ELEM_IFACE_CARD;
index 1ba887c..ebc714b 100644 (file)
@@ -32,10 +32,8 @@ int snd_oss_info_register(int dev, int num, char *string)
        mutex_lock(&strings);
        if (string == NULL) {
                x = snd_sndstat_strings[num][dev];
-               if (x) {
-                       kfree(x);
-                       x = NULL;
-               }
+               kfree(x);
+               x = NULL;
        } else {
                x = kstrdup(string, GFP_KERNEL);
                if (x == NULL) {
index 32350c6..d1e3055 100644 (file)
@@ -62,10 +62,13 @@ static int snd_jack_dev_free(struct snd_device *device)
        struct snd_card *card = device->card;
        struct snd_jack_kctl *jack_kctl, *tmp_jack_kctl;
 
+       down_write(&card->controls_rwsem);
        list_for_each_entry_safe(jack_kctl, tmp_jack_kctl, &jack->kctl_list, list) {
                list_del_init(&jack_kctl->list);
                snd_ctl_remove(card, jack_kctl->kctl);
        }
+       up_write(&card->controls_rwsem);
+
        if (jack->private_free)
                jack->private_free(jack);
 
@@ -509,6 +512,10 @@ int snd_jack_new(struct snd_card *card, const char *id, int type,
                return -ENOMEM;
 
        jack->id = kstrdup(id, GFP_KERNEL);
+       if (jack->id == NULL) {
+               kfree(jack);
+               return -ENOMEM;
+       }
 
        /* don't creat input device for phantom jack */
        if (!phantom_jack) {
index 9fc971a..d1fcd1d 100644 (file)
@@ -620,6 +620,52 @@ static const struct snd_malloc_ops snd_dma_noncontig_ops = {
        .get_chunk_size = snd_dma_noncontig_get_chunk_size,
 };
 
+/* x86-specific SG-buffer with WC pages */
+#ifdef CONFIG_SND_DMA_SGBUF
+#define sg_wc_address(it) ((unsigned long)page_address(sg_page_iter_page(it)))
+
+static void *snd_dma_sg_wc_alloc(struct snd_dma_buffer *dmab, size_t size)
+{
+       void *p = snd_dma_noncontig_alloc(dmab, size);
+       struct sg_table *sgt = dmab->private_data;
+       struct sg_page_iter iter;
+
+       if (!p)
+               return NULL;
+       for_each_sgtable_page(sgt, &iter, 0)
+               set_memory_wc(sg_wc_address(&iter), 1);
+       return p;
+}
+
+static void snd_dma_sg_wc_free(struct snd_dma_buffer *dmab)
+{
+       struct sg_table *sgt = dmab->private_data;
+       struct sg_page_iter iter;
+
+       for_each_sgtable_page(sgt, &iter, 0)
+               set_memory_wb(sg_wc_address(&iter), 1);
+       snd_dma_noncontig_free(dmab);
+}
+
+static int snd_dma_sg_wc_mmap(struct snd_dma_buffer *dmab,
+                             struct vm_area_struct *area)
+{
+       area->vm_page_prot = pgprot_writecombine(area->vm_page_prot);
+       return dma_mmap_noncontiguous(dmab->dev.dev, area,
+                                     dmab->bytes, dmab->private_data);
+}
+
+static const struct snd_malloc_ops snd_dma_sg_wc_ops = {
+       .alloc = snd_dma_sg_wc_alloc,
+       .free = snd_dma_sg_wc_free,
+       .mmap = snd_dma_sg_wc_mmap,
+       .sync = snd_dma_noncontig_sync,
+       .get_addr = snd_dma_noncontig_get_addr,
+       .get_page = snd_dma_noncontig_get_page,
+       .get_chunk_size = snd_dma_noncontig_get_chunk_size,
+};
+#endif /* CONFIG_SND_DMA_SGBUF */
+
 /*
  * Non-coherent pages allocator
  */
@@ -679,14 +725,13 @@ static const struct snd_malloc_ops *dma_ops[] = {
        [SNDRV_DMA_TYPE_DEV_WC] = &snd_dma_wc_ops,
        [SNDRV_DMA_TYPE_NONCONTIG] = &snd_dma_noncontig_ops,
        [SNDRV_DMA_TYPE_NONCOHERENT] = &snd_dma_noncoherent_ops,
+#ifdef CONFIG_SND_DMA_SGBUF
+       [SNDRV_DMA_TYPE_DEV_WC_SG] = &snd_dma_sg_wc_ops,
+#endif
 #ifdef CONFIG_GENERIC_ALLOCATOR
        [SNDRV_DMA_TYPE_DEV_IRAM] = &snd_dma_iram_ops,
 #endif /* CONFIG_GENERIC_ALLOCATOR */
 #endif /* CONFIG_HAS_DMA */
-#ifdef CONFIG_SND_DMA_SGBUF
-       [SNDRV_DMA_TYPE_DEV_SG] = &snd_dma_sg_ops,
-       [SNDRV_DMA_TYPE_DEV_WC_SG] = &snd_dma_sg_ops,
-#endif
 };
 
 static const struct snd_malloc_ops *snd_dma_get_ops(struct snd_dma_buffer *dmab)
index 82a8187..3ee9edf 100644 (file)
@@ -147,7 +147,7 @@ snd_pcm_hw_param_value_min(const struct snd_pcm_hw_params *params,
  *
  * Return the maximum value for field PAR.
  */
-static unsigned int
+static int
 snd_pcm_hw_param_value_max(const struct snd_pcm_hw_params *params,
                           snd_pcm_hw_param_t var, int *dir)
 {
@@ -682,18 +682,24 @@ static int snd_pcm_oss_period_size(struct snd_pcm_substream *substream,
                                   struct snd_pcm_hw_params *oss_params,
                                   struct snd_pcm_hw_params *slave_params)
 {
-       size_t s;
-       size_t oss_buffer_size, oss_period_size, oss_periods;
-       size_t min_period_size, max_period_size;
+       ssize_t s;
+       ssize_t oss_buffer_size;
+       ssize_t oss_period_size, oss_periods;
+       ssize_t min_period_size, max_period_size;
        struct snd_pcm_runtime *runtime = substream->runtime;
        size_t oss_frame_size;
 
        oss_frame_size = snd_pcm_format_physical_width(params_format(oss_params)) *
                         params_channels(oss_params) / 8;
 
+       oss_buffer_size = snd_pcm_hw_param_value_max(slave_params,
+                                                    SNDRV_PCM_HW_PARAM_BUFFER_SIZE,
+                                                    NULL);
+       if (oss_buffer_size <= 0)
+               return -EINVAL;
        oss_buffer_size = snd_pcm_plug_client_size(substream,
-                                                  snd_pcm_hw_param_value_max(slave_params, SNDRV_PCM_HW_PARAM_BUFFER_SIZE, NULL)) * oss_frame_size;
-       if (!oss_buffer_size)
+                                                  oss_buffer_size * oss_frame_size);
+       if (oss_buffer_size <= 0)
                return -EINVAL;
        oss_buffer_size = rounddown_pow_of_two(oss_buffer_size);
        if (atomic_read(&substream->mmap_count)) {
@@ -730,7 +736,7 @@ static int snd_pcm_oss_period_size(struct snd_pcm_substream *substream,
 
        min_period_size = snd_pcm_plug_client_size(substream,
                                                   snd_pcm_hw_param_value_min(slave_params, SNDRV_PCM_HW_PARAM_PERIOD_SIZE, NULL));
-       if (min_period_size) {
+       if (min_period_size > 0) {
                min_period_size *= oss_frame_size;
                min_period_size = roundup_pow_of_two(min_period_size);
                if (oss_period_size < min_period_size)
@@ -739,7 +745,7 @@ static int snd_pcm_oss_period_size(struct snd_pcm_substream *substream,
 
        max_period_size = snd_pcm_plug_client_size(substream,
                                                   snd_pcm_hw_param_value_max(slave_params, SNDRV_PCM_HW_PARAM_PERIOD_SIZE, NULL));
-       if (max_period_size) {
+       if (max_period_size > 0) {
                max_period_size *= oss_frame_size;
                max_period_size = rounddown_pow_of_two(max_period_size);
                if (oss_period_size > max_period_size)
@@ -752,7 +758,7 @@ static int snd_pcm_oss_period_size(struct snd_pcm_substream *substream,
                oss_periods = substream->oss.setup.periods;
 
        s = snd_pcm_hw_param_value_max(slave_params, SNDRV_PCM_HW_PARAM_PERIODS, NULL);
-       if (runtime->oss.maxfrags && s > runtime->oss.maxfrags)
+       if (s > 0 && runtime->oss.maxfrags && s > runtime->oss.maxfrags)
                s = runtime->oss.maxfrags;
        if (oss_periods > s)
                oss_periods = s;
@@ -878,8 +884,15 @@ static int snd_pcm_oss_change_params_locked(struct snd_pcm_substream *substream)
                err = -EINVAL;
                goto failure;
        }
-       choose_rate(substream, sparams, runtime->oss.rate);
-       snd_pcm_hw_param_near(substream, sparams, SNDRV_PCM_HW_PARAM_CHANNELS, runtime->oss.channels, NULL);
+
+       err = choose_rate(substream, sparams, runtime->oss.rate);
+       if (err < 0)
+               goto failure;
+       err = snd_pcm_hw_param_near(substream, sparams,
+                                   SNDRV_PCM_HW_PARAM_CHANNELS,
+                                   runtime->oss.channels, NULL);
+       if (err < 0)
+               goto failure;
 
        format = snd_pcm_oss_format_from(runtime->oss.format);
 
@@ -1956,7 +1969,7 @@ static int snd_pcm_oss_set_fragment1(struct snd_pcm_substream *substream, unsign
        if (runtime->oss.subdivision || runtime->oss.fragshift)
                return -EINVAL;
        fragshift = val & 0xffff;
-       if (fragshift >= 31)
+       if (fragshift >= 25) /* should be large enough */
                return -EINVAL;
        runtime->oss.fragshift = fragshift;
        runtime->oss.maxfrags = (val >> 16) & 0xffff;
@@ -2052,7 +2065,7 @@ static int snd_pcm_oss_set_trigger(struct snd_pcm_oss_file *pcm_oss_file, int tr
        int err, cmd;
 
 #ifdef OSS_DEBUG
-       pcm_dbg(substream->pcm, "pcm_oss: trigger = 0x%x\n", trigger);
+       pr_debug("pcm_oss: trigger = 0x%x\n", trigger);
 #endif
        
        psubstream = pcm_oss_file->streams[SNDRV_PCM_STREAM_PLAYBACK];
index 6fd3677..ba4a987 100644 (file)
@@ -810,7 +810,11 @@ EXPORT_SYMBOL(snd_pcm_new_internal);
 static void free_chmap(struct snd_pcm_str *pstr)
 {
        if (pstr->chmap_kctl) {
-               snd_ctl_remove(pstr->pcm->card, pstr->chmap_kctl);
+               struct snd_card *card = pstr->pcm->card;
+
+               down_write(&card->controls_rwsem);
+               snd_ctl_remove(card, pstr->chmap_kctl);
+               up_write(&card->controls_rwsem);
                pstr->chmap_kctl = NULL;
        }
 }
index 6f30231..befa980 100644 (file)
@@ -447,6 +447,7 @@ static int snd_rawmidi_open(struct inode *inode, struct file *file)
                err = -ENOMEM;
                goto __error;
        }
+       rawmidi_file->user_pversion = 0;
        init_waitqueue_entry(&wait, current);
        add_wait_queue(&rmidi->open_wait, &wait);
        while (1) {
index d6c02de..bc93310 100644 (file)
@@ -235,12 +235,15 @@ struct snd_seq_queue *snd_seq_queue_find_name(char *name)
 
 /* -------------------------------------------------------- */
 
+#define MAX_CELL_PROCESSES_IN_QUEUE    1000
+
 void snd_seq_check_queue(struct snd_seq_queue *q, int atomic, int hop)
 {
        unsigned long flags;
        struct snd_seq_event_cell *cell;
        snd_seq_tick_time_t cur_tick;
        snd_seq_real_time_t cur_time;
+       int processed = 0;
 
        if (q == NULL)
                return;
@@ -263,6 +266,8 @@ void snd_seq_check_queue(struct snd_seq_queue *q, int atomic, int hop)
                if (!cell)
                        break;
                snd_seq_dispatch_event(cell, atomic, hop);
+               if (++processed >= MAX_CELL_PROCESSES_IN_QUEUE)
+                       goto out; /* the rest processed at the next batch */
        }
 
        /* Process time queue... */
@@ -272,14 +277,19 @@ void snd_seq_check_queue(struct snd_seq_queue *q, int atomic, int hop)
                if (!cell)
                        break;
                snd_seq_dispatch_event(cell, atomic, hop);
+               if (++processed >= MAX_CELL_PROCESSES_IN_QUEUE)
+                       goto out; /* the rest processed at the next batch */
        }
 
+ out:
        /* free lock */
        spin_lock_irqsave(&q->check_lock, flags);
        if (q->check_again) {
                q->check_again = 0;
-               spin_unlock_irqrestore(&q->check_lock, flags);
-               goto __again;
+               if (processed < MAX_CELL_PROCESSES_IN_QUEUE) {
+                       spin_unlock_irqrestore(&q->check_lock, flags);
+                       goto __again;
+               }
        }
        q->check_blocked = 0;
        spin_unlock_irqrestore(&q->check_lock, flags);
diff --git a/sound/core/sgbuf.c b/sound/core/sgbuf.c
deleted file mode 100644 (file)
index 8352a5c..0000000
+++ /dev/null
@@ -1,201 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0-or-later
-/*
- * Scatter-Gather buffer
- *
- *  Copyright (c) by Takashi Iwai <tiwai@suse.de>
- */
-
-#include <linux/slab.h>
-#include <linux/mm.h>
-#include <linux/vmalloc.h>
-#include <linux/export.h>
-#include <sound/memalloc.h>
-#include "memalloc_local.h"
-
-struct snd_sg_page {
-       void *buf;
-       dma_addr_t addr;
-};
-
-struct snd_sg_buf {
-       int size;       /* allocated byte size */
-       int pages;      /* allocated pages */
-       int tblsize;    /* allocated table size */
-       struct snd_sg_page *table;      /* address table */
-       struct page **page_table;       /* page table (for vmap/vunmap) */
-       struct device *dev;
-};
-
-/* table entries are align to 32 */
-#define SGBUF_TBL_ALIGN                32
-#define sgbuf_align_table(tbl) ALIGN((tbl), SGBUF_TBL_ALIGN)
-
-static void snd_dma_sg_free(struct snd_dma_buffer *dmab)
-{
-       struct snd_sg_buf *sgbuf = dmab->private_data;
-       struct snd_dma_buffer tmpb;
-       int i;
-
-       if (!sgbuf)
-               return;
-
-       vunmap(dmab->area);
-       dmab->area = NULL;
-
-       tmpb.dev.type = SNDRV_DMA_TYPE_DEV;
-       if (dmab->dev.type == SNDRV_DMA_TYPE_DEV_WC_SG)
-               tmpb.dev.type = SNDRV_DMA_TYPE_DEV_WC;
-       tmpb.dev.dev = sgbuf->dev;
-       for (i = 0; i < sgbuf->pages; i++) {
-               if (!(sgbuf->table[i].addr & ~PAGE_MASK))
-                       continue; /* continuous pages */
-               tmpb.area = sgbuf->table[i].buf;
-               tmpb.addr = sgbuf->table[i].addr & PAGE_MASK;
-               tmpb.bytes = (sgbuf->table[i].addr & ~PAGE_MASK) << PAGE_SHIFT;
-               snd_dma_free_pages(&tmpb);
-       }
-
-       kfree(sgbuf->table);
-       kfree(sgbuf->page_table);
-       kfree(sgbuf);
-       dmab->private_data = NULL;
-}
-
-#define MAX_ALLOC_PAGES                32
-
-static void *snd_dma_sg_alloc(struct snd_dma_buffer *dmab, size_t size)
-{
-       struct snd_sg_buf *sgbuf;
-       unsigned int i, pages, chunk, maxpages;
-       struct snd_dma_buffer tmpb;
-       struct snd_sg_page *table;
-       struct page **pgtable;
-       int type = SNDRV_DMA_TYPE_DEV;
-       pgprot_t prot = PAGE_KERNEL;
-       void *area;
-
-       dmab->private_data = sgbuf = kzalloc(sizeof(*sgbuf), GFP_KERNEL);
-       if (!sgbuf)
-               return NULL;
-       if (dmab->dev.type == SNDRV_DMA_TYPE_DEV_WC_SG) {
-               type = SNDRV_DMA_TYPE_DEV_WC;
-#ifdef pgprot_noncached
-               prot = pgprot_noncached(PAGE_KERNEL);
-#endif
-       }
-       sgbuf->dev = dmab->dev.dev;
-       pages = snd_sgbuf_aligned_pages(size);
-       sgbuf->tblsize = sgbuf_align_table(pages);
-       table = kcalloc(sgbuf->tblsize, sizeof(*table), GFP_KERNEL);
-       if (!table)
-               goto _failed;
-       sgbuf->table = table;
-       pgtable = kcalloc(sgbuf->tblsize, sizeof(*pgtable), GFP_KERNEL);
-       if (!pgtable)
-               goto _failed;
-       sgbuf->page_table = pgtable;
-
-       /* allocate pages */
-       maxpages = MAX_ALLOC_PAGES;
-       while (pages > 0) {
-               chunk = pages;
-               /* don't be too eager to take a huge chunk */
-               if (chunk > maxpages)
-                       chunk = maxpages;
-               chunk <<= PAGE_SHIFT;
-               if (snd_dma_alloc_pages_fallback(type, dmab->dev.dev,
-                                                chunk, &tmpb) < 0) {
-                       if (!sgbuf->pages)
-                               goto _failed;
-                       size = sgbuf->pages * PAGE_SIZE;
-                       break;
-               }
-               chunk = tmpb.bytes >> PAGE_SHIFT;
-               for (i = 0; i < chunk; i++) {
-                       table->buf = tmpb.area;
-                       table->addr = tmpb.addr;
-                       if (!i)
-                               table->addr |= chunk; /* mark head */
-                       table++;
-                       *pgtable++ = virt_to_page(tmpb.area);
-                       tmpb.area += PAGE_SIZE;
-                       tmpb.addr += PAGE_SIZE;
-               }
-               sgbuf->pages += chunk;
-               pages -= chunk;
-               if (chunk < maxpages)
-                       maxpages = chunk;
-       }
-
-       sgbuf->size = size;
-       area = vmap(sgbuf->page_table, sgbuf->pages, VM_MAP, prot);
-       if (!area)
-               goto _failed;
-       return area;
-
- _failed:
-       snd_dma_sg_free(dmab); /* free the table */
-       return NULL;
-}
-
-static dma_addr_t snd_dma_sg_get_addr(struct snd_dma_buffer *dmab,
-                                     size_t offset)
-{
-       struct snd_sg_buf *sgbuf = dmab->private_data;
-       dma_addr_t addr;
-
-       addr = sgbuf->table[offset >> PAGE_SHIFT].addr;
-       addr &= ~((dma_addr_t)PAGE_SIZE - 1);
-       return addr + offset % PAGE_SIZE;
-}
-
-static struct page *snd_dma_sg_get_page(struct snd_dma_buffer *dmab,
-                                       size_t offset)
-{
-       struct snd_sg_buf *sgbuf = dmab->private_data;
-       unsigned int idx = offset >> PAGE_SHIFT;
-
-       if (idx >= (unsigned int)sgbuf->pages)
-               return NULL;
-       return sgbuf->page_table[idx];
-}
-
-static unsigned int snd_dma_sg_get_chunk_size(struct snd_dma_buffer *dmab,
-                                             unsigned int ofs,
-                                             unsigned int size)
-{
-       struct snd_sg_buf *sg = dmab->private_data;
-       unsigned int start, end, pg;
-
-       start = ofs >> PAGE_SHIFT;
-       end = (ofs + size - 1) >> PAGE_SHIFT;
-       /* check page continuity */
-       pg = sg->table[start].addr >> PAGE_SHIFT;
-       for (;;) {
-               start++;
-               if (start > end)
-                       break;
-               pg++;
-               if ((sg->table[start].addr >> PAGE_SHIFT) != pg)
-                       return (start << PAGE_SHIFT) - ofs;
-       }
-       /* ok, all on continuous pages */
-       return size;
-}
-
-static int snd_dma_sg_mmap(struct snd_dma_buffer *dmab,
-                          struct vm_area_struct *area)
-{
-       if (dmab->dev.type == SNDRV_DMA_TYPE_DEV_WC_SG)
-               area->vm_page_prot = pgprot_writecombine(area->vm_page_prot);
-       return -ENOENT; /* continue with the default mmap handler */
-}
-
-const struct snd_malloc_ops snd_dma_sg_ops = {
-       .alloc = snd_dma_sg_alloc,
-       .free = snd_dma_sg_free,
-       .get_addr = snd_dma_sg_get_addr,
-       .get_page = snd_dma_sg_get_page,
-       .get_chunk_size = snd_dma_sg_get_chunk_size,
-       .mmap = snd_dma_sg_mmap,
-};
index e1b69c6..e2b7be6 100644 (file)
@@ -397,7 +397,7 @@ void snd_opl3_note_on(void *p, int note, int vel, struct snd_midi_channel *chan)
        }
        if (instr_4op) {
                vp2 = &opl3->voices[voice + 3];
-               if (vp->state > 0) {
+               if (vp2->state > 0) {
                        opl3_reg = reg_side | (OPL3_REG_KEYON_BLOCK +
                                               voice_offset + 3);
                        reg_val = vp->keyon_reg & ~OPL3_KEYON_BIT;
index 37154ed..d2b5724 100644 (file)
@@ -18,7 +18,7 @@
 /**
  * snd_hdac_ext_stream_init - initialize each stream (aka device)
  * @bus: HD-audio core bus
- * @stream: HD-audio ext core stream object to initialize
+ * @hext_stream: HD-audio ext core stream object to initialize
  * @idx: stream index number
  * @direction: stream direction (SNDRV_PCM_STREAM_PLAYBACK or SNDRV_PCM_STREAM_CAPTURE)
  * @tag: the tag id to assign
  * invoke hdac stream initialization routine
  */
 void snd_hdac_ext_stream_init(struct hdac_bus *bus,
-                               struct hdac_ext_stream *stream,
-                               int idx, int direction, int tag)
+                             struct hdac_ext_stream *hext_stream,
+                             int idx, int direction, int tag)
 {
        if (bus->ppcap) {
-               stream->pphc_addr = bus->ppcap + AZX_PPHC_BASE +
+               hext_stream->pphc_addr = bus->ppcap + AZX_PPHC_BASE +
                                AZX_PPHC_INTERVAL * idx;
 
-               stream->pplc_addr = bus->ppcap + AZX_PPLC_BASE +
+               hext_stream->pplc_addr = bus->ppcap + AZX_PPLC_BASE +
                                AZX_PPLC_MULTI * bus->num_streams +
                                AZX_PPLC_INTERVAL * idx;
        }
 
        if (bus->spbcap) {
-               stream->spib_addr = bus->spbcap + AZX_SPB_BASE +
+               hext_stream->spib_addr = bus->spbcap + AZX_SPB_BASE +
                                        AZX_SPB_INTERVAL * idx +
                                        AZX_SPB_SPIB;
 
-               stream->fifo_addr = bus->spbcap + AZX_SPB_BASE +
+               hext_stream->fifo_addr = bus->spbcap + AZX_SPB_BASE +
                                        AZX_SPB_INTERVAL * idx +
                                        AZX_SPB_MAXFIFO;
        }
 
        if (bus->drsmcap)
-               stream->dpibr_addr = bus->drsmcap + AZX_DRSM_BASE +
+               hext_stream->dpibr_addr = bus->drsmcap + AZX_DRSM_BASE +
                                        AZX_DRSM_INTERVAL * idx;
 
-       stream->decoupled = false;
-       snd_hdac_stream_init(bus, &stream->hstream, idx, direction, tag);
+       hext_stream->decoupled = false;
+       snd_hdac_stream_init(bus, &hext_stream->hstream, idx, direction, tag);
 }
 EXPORT_SYMBOL_GPL(snd_hdac_ext_stream_init);
 
@@ -67,18 +67,18 @@ EXPORT_SYMBOL_GPL(snd_hdac_ext_stream_init);
  * @dir: direction of streams
  */
 int snd_hdac_ext_stream_init_all(struct hdac_bus *bus, int start_idx,
-               int num_stream, int dir)
+                                int num_stream, int dir)
 {
        int stream_tag = 0;
        int i, tag, idx = start_idx;
 
        for (i = 0; i < num_stream; i++) {
-               struct hdac_ext_stream *stream =
-                               kzalloc(sizeof(*stream), GFP_KERNEL);
-               if (!stream)
+               struct hdac_ext_stream *hext_stream =
+                               kzalloc(sizeof(*hext_stream), GFP_KERNEL);
+               if (!hext_stream)
                        return -ENOMEM;
                tag = ++stream_tag;
-               snd_hdac_ext_stream_init(bus, stream, idx, dir, tag);
+               snd_hdac_ext_stream_init(bus, hext_stream, idx, dir, tag);
                idx++;
        }
 
@@ -95,22 +95,22 @@ EXPORT_SYMBOL_GPL(snd_hdac_ext_stream_init_all);
 void snd_hdac_stream_free_all(struct hdac_bus *bus)
 {
        struct hdac_stream *s, *_s;
-       struct hdac_ext_stream *stream;
+       struct hdac_ext_stream *hext_stream;
 
        list_for_each_entry_safe(s, _s, &bus->stream_list, list) {
-               stream = stream_to_hdac_ext_stream(s);
-               snd_hdac_ext_stream_decouple(bus, stream, false);
+               hext_stream = stream_to_hdac_ext_stream(s);
+               snd_hdac_ext_stream_decouple(bus, hext_stream, false);
                list_del(&s->list);
-               kfree(stream);
+               kfree(hext_stream);
        }
 }
 EXPORT_SYMBOL_GPL(snd_hdac_stream_free_all);
 
 void snd_hdac_ext_stream_decouple_locked(struct hdac_bus *bus,
-                                        struct hdac_ext_stream *stream,
+                                        struct hdac_ext_stream *hext_stream,
                                         bool decouple)
 {
-       struct hdac_stream *hstream = &stream->hstream;
+       struct hdac_stream *hstream = &hext_stream->hstream;
        u32 val;
        int mask = AZX_PPCTL_PROCEN(hstream->index);
 
@@ -121,76 +121,76 @@ void snd_hdac_ext_stream_decouple_locked(struct hdac_bus *bus,
        else if (!decouple && val)
                snd_hdac_updatel(bus->ppcap, AZX_REG_PP_PPCTL, mask, 0);
 
-       stream->decoupled = decouple;
+       hext_stream->decoupled = decouple;
 }
 EXPORT_SYMBOL_GPL(snd_hdac_ext_stream_decouple_locked);
 
 /**
  * snd_hdac_ext_stream_decouple - decouple the hdac stream
  * @bus: HD-audio core bus
- * @stream: HD-audio ext core stream object to initialize
+ * @hext_stream: HD-audio ext core stream object to initialize
  * @decouple: flag to decouple
  */
 void snd_hdac_ext_stream_decouple(struct hdac_bus *bus,
-                                 struct hdac_ext_stream *stream, bool decouple)
+                                 struct hdac_ext_stream *hext_stream, bool decouple)
 {
        spin_lock_irq(&bus->reg_lock);
-       snd_hdac_ext_stream_decouple_locked(bus, stream, decouple);
+       snd_hdac_ext_stream_decouple_locked(bus, hext_stream, decouple);
        spin_unlock_irq(&bus->reg_lock);
 }
 EXPORT_SYMBOL_GPL(snd_hdac_ext_stream_decouple);
 
 /**
  * snd_hdac_ext_link_stream_start - start a stream
- * @stream: HD-audio ext core stream to start
+ * @hext_stream: HD-audio ext core stream to start
  */
-void snd_hdac_ext_link_stream_start(struct hdac_ext_stream *stream)
+void snd_hdac_ext_link_stream_start(struct hdac_ext_stream *hext_stream)
 {
-       snd_hdac_updatel(stream->pplc_addr, AZX_REG_PPLCCTL,
+       snd_hdac_updatel(hext_stream->pplc_addr, AZX_REG_PPLCCTL,
                         AZX_PPLCCTL_RUN, AZX_PPLCCTL_RUN);
 }
 EXPORT_SYMBOL_GPL(snd_hdac_ext_link_stream_start);
 
 /**
  * snd_hdac_ext_link_stream_clear - stop a stream DMA
- * @stream: HD-audio ext core stream to stop
+ * @hext_stream: HD-audio ext core stream to stop
  */
-void snd_hdac_ext_link_stream_clear(struct hdac_ext_stream *stream)
+void snd_hdac_ext_link_stream_clear(struct hdac_ext_stream *hext_stream)
 {
-       snd_hdac_updatel(stream->pplc_addr, AZX_REG_PPLCCTL, AZX_PPLCCTL_RUN, 0);
+       snd_hdac_updatel(hext_stream->pplc_addr, AZX_REG_PPLCCTL, AZX_PPLCCTL_RUN, 0);
 }
 EXPORT_SYMBOL_GPL(snd_hdac_ext_link_stream_clear);
 
 /**
  * snd_hdac_ext_link_stream_reset - reset a stream
- * @stream: HD-audio ext core stream to reset
+ * @hext_stream: HD-audio ext core stream to reset
  */
-void snd_hdac_ext_link_stream_reset(struct hdac_ext_stream *stream)
+void snd_hdac_ext_link_stream_reset(struct hdac_ext_stream *hext_stream)
 {
        unsigned char val;
        int timeout;
 
-       snd_hdac_ext_link_stream_clear(stream);
+       snd_hdac_ext_link_stream_clear(hext_stream);
 
-       snd_hdac_updatel(stream->pplc_addr, AZX_REG_PPLCCTL,
+       snd_hdac_updatel(hext_stream->pplc_addr, AZX_REG_PPLCCTL,
                         AZX_PPLCCTL_STRST, AZX_PPLCCTL_STRST);
        udelay(3);
        timeout = 50;
        do {
-               val = readl(stream->pplc_addr + AZX_REG_PPLCCTL) &
+               val = readl(hext_stream->pplc_addr + AZX_REG_PPLCCTL) &
                                AZX_PPLCCTL_STRST;
                if (val)
                        break;
                udelay(3);
        } while (--timeout);
        val &= ~AZX_PPLCCTL_STRST;
-       writel(val, stream->pplc_addr + AZX_REG_PPLCCTL);
+       writel(val, hext_stream->pplc_addr + AZX_REG_PPLCCTL);
        udelay(3);
 
        timeout = 50;
        /* waiting for hardware to report that the stream is out of reset */
        do {
-               val = readl(stream->pplc_addr + AZX_REG_PPLCCTL) & AZX_PPLCCTL_STRST;
+               val = readl(hext_stream->pplc_addr + AZX_REG_PPLCCTL) & AZX_PPLCCTL_STRST;
                if (!val)
                        break;
                udelay(3);
@@ -201,24 +201,24 @@ EXPORT_SYMBOL_GPL(snd_hdac_ext_link_stream_reset);
 
 /**
  * snd_hdac_ext_link_stream_setup -  set up the SD for streaming
- * @stream: HD-audio ext core stream to set up
+ * @hext_stream: HD-audio ext core stream to set up
  * @fmt: stream format
  */
-int snd_hdac_ext_link_stream_setup(struct hdac_ext_stream *stream, int fmt)
+int snd_hdac_ext_link_stream_setup(struct hdac_ext_stream *hext_stream, int fmt)
 {
-       struct hdac_stream *hstream = &stream->hstream;
+       struct hdac_stream *hstream = &hext_stream->hstream;
        unsigned int val;
 
        /* make sure the run bit is zero for SD */
-       snd_hdac_ext_link_stream_clear(stream);
+       snd_hdac_ext_link_stream_clear(hext_stream);
        /* program the stream_tag */
-       val = readl(stream->pplc_addr + AZX_REG_PPLCCTL);
+       val = readl(hext_stream->pplc_addr + AZX_REG_PPLCCTL);
        val = (val & ~AZX_PPLCCTL_STRM_MASK) |
                (hstream->stream_tag << AZX_PPLCCTL_STRM_SHIFT);
-       writel(val, stream->pplc_addr + AZX_REG_PPLCCTL);
+       writel(val, hext_stream->pplc_addr + AZX_REG_PPLCCTL);
 
        /* program the stream format */
-       writew(fmt, stream->pplc_addr + AZX_REG_PPLCFMT);
+       writew(fmt, hext_stream->pplc_addr + AZX_REG_PPLCFMT);
 
        return 0;
 }
@@ -230,7 +230,7 @@ EXPORT_SYMBOL_GPL(snd_hdac_ext_link_stream_setup);
  * @stream: stream id
  */
 void snd_hdac_ext_link_set_stream_id(struct hdac_ext_link *link,
-                                int stream)
+                                    int stream)
 {
        snd_hdac_updatew(link->ml_addr, AZX_REG_ML_LOSIDV, (1 << stream), 1 << stream);
 }
@@ -250,10 +250,10 @@ EXPORT_SYMBOL_GPL(snd_hdac_ext_link_clear_stream_id);
 
 static struct hdac_ext_stream *
 hdac_ext_link_stream_assign(struct hdac_bus *bus,
-                               struct snd_pcm_substream *substream)
+                           struct snd_pcm_substream *substream)
 {
        struct hdac_ext_stream *res = NULL;
-       struct hdac_stream *stream = NULL;
+       struct hdac_stream *hstream = NULL;
 
        if (!bus->ppcap) {
                dev_err(bus->dev, "stream type not supported\n");
@@ -261,22 +261,22 @@ hdac_ext_link_stream_assign(struct hdac_bus *bus,
        }
 
        spin_lock_irq(&bus->reg_lock);
-       list_for_each_entry(stream, &bus->stream_list, list) {
-               struct hdac_ext_stream *hstream = container_of(stream,
-                                               struct hdac_ext_stream,
-                                               hstream);
-               if (stream->direction != substream->stream)
+       list_for_each_entry(hstream, &bus->stream_list, list) {
+               struct hdac_ext_stream *hext_stream = container_of(hstream,
+                                                                struct hdac_ext_stream,
+                                                                hstream);
+               if (hstream->direction != substream->stream)
                        continue;
 
                /* check if decoupled stream and not in use is available */
-               if (hstream->decoupled && !hstream->link_locked) {
-                       res = hstream;
+               if (hext_stream->decoupled && !hext_stream->link_locked) {
+                       res = hext_stream;
                        break;
                }
 
-               if (!hstream->link_locked) {
-                       snd_hdac_ext_stream_decouple_locked(bus, hstream, true);
-                       res = hstream;
+               if (!hext_stream->link_locked) {
+                       snd_hdac_ext_stream_decouple_locked(bus, hext_stream, true);
+                       res = hext_stream;
                        break;
                }
        }
@@ -290,10 +290,10 @@ hdac_ext_link_stream_assign(struct hdac_bus *bus,
 
 static struct hdac_ext_stream *
 hdac_ext_host_stream_assign(struct hdac_bus *bus,
-                               struct snd_pcm_substream *substream)
+                           struct snd_pcm_substream *substream)
 {
        struct hdac_ext_stream *res = NULL;
-       struct hdac_stream *stream = NULL;
+       struct hdac_stream *hstream = NULL;
 
        if (!bus->ppcap) {
                dev_err(bus->dev, "stream type not supported\n");
@@ -301,17 +301,17 @@ hdac_ext_host_stream_assign(struct hdac_bus *bus,
        }
 
        spin_lock_irq(&bus->reg_lock);
-       list_for_each_entry(stream, &bus->stream_list, list) {
-               struct hdac_ext_stream *hstream = container_of(stream,
-                                               struct hdac_ext_stream,
-                                               hstream);
-               if (stream->direction != substream->stream)
+       list_for_each_entry(hstream, &bus->stream_list, list) {
+               struct hdac_ext_stream *hext_stream = container_of(hstream,
+                                                                struct hdac_ext_stream,
+                                                                hstream);
+               if (hstream->direction != substream->stream)
                        continue;
 
-               if (!stream->opened) {
-                       if (!hstream->decoupled)
-                               snd_hdac_ext_stream_decouple_locked(bus, hstream, true);
-                       res = hstream;
+               if (!hstream->opened) {
+                       if (!hext_stream->decoupled)
+                               snd_hdac_ext_stream_decouple_locked(bus, hext_stream, true);
+                       res = hext_stream;
                        break;
                }
        }
@@ -346,16 +346,17 @@ struct hdac_ext_stream *snd_hdac_ext_stream_assign(struct hdac_bus *bus,
                                           struct snd_pcm_substream *substream,
                                           int type)
 {
-       struct hdac_ext_stream *hstream = NULL;
-       struct hdac_stream *stream = NULL;
+       struct hdac_ext_stream *hext_stream = NULL;
+       struct hdac_stream *hstream = NULL;
 
        switch (type) {
        case HDAC_EXT_STREAM_TYPE_COUPLED:
-               stream = snd_hdac_stream_assign(bus, substream);
-               if (stream)
-                       hstream = container_of(stream,
-                                       struct hdac_ext_stream, hstream);
-               return hstream;
+               hstream = snd_hdac_stream_assign(bus, substream);
+               if (hstream)
+                       hext_stream = container_of(hstream,
+                                                  struct hdac_ext_stream,
+                                                  hstream);
+               return hext_stream;
 
        case HDAC_EXT_STREAM_TYPE_HOST:
                return hdac_ext_host_stream_assign(bus, substream);
@@ -371,34 +372,34 @@ EXPORT_SYMBOL_GPL(snd_hdac_ext_stream_assign);
 
 /**
  * snd_hdac_ext_stream_release - release the assigned stream
- * @stream: HD-audio ext core stream to release
+ * @hext_stream: HD-audio ext core stream to release
  * @type: type of stream (coupled, host or link stream)
  *
  * Release the stream that has been assigned by snd_hdac_ext_stream_assign().
  */
-void snd_hdac_ext_stream_release(struct hdac_ext_stream *stream, int type)
+void snd_hdac_ext_stream_release(struct hdac_ext_stream *hext_stream, int type)
 {
-       struct hdac_bus *bus = stream->hstream.bus;
+       struct hdac_bus *bus = hext_stream->hstream.bus;
 
        switch (type) {
        case HDAC_EXT_STREAM_TYPE_COUPLED:
-               snd_hdac_stream_release(&stream->hstream);
+               snd_hdac_stream_release(&hext_stream->hstream);
                break;
 
        case HDAC_EXT_STREAM_TYPE_HOST:
                spin_lock_irq(&bus->reg_lock);
-               if (stream->decoupled && !stream->link_locked)
-                       snd_hdac_ext_stream_decouple_locked(bus, stream, false);
+               if (hext_stream->decoupled && !hext_stream->link_locked)
+                       snd_hdac_ext_stream_decouple_locked(bus, hext_stream, false);
                spin_unlock_irq(&bus->reg_lock);
-               snd_hdac_stream_release(&stream->hstream);
+               snd_hdac_stream_release(&hext_stream->hstream);
                break;
 
        case HDAC_EXT_STREAM_TYPE_LINK:
                spin_lock_irq(&bus->reg_lock);
-               if (stream->decoupled && !stream->hstream.opened)
-                       snd_hdac_ext_stream_decouple_locked(bus, stream, false);
-               stream->link_locked = 0;
-               stream->link_substream = NULL;
+               if (hext_stream->decoupled && !hext_stream->hstream.opened)
+                       snd_hdac_ext_stream_decouple_locked(bus, hext_stream, false);
+               hext_stream->link_locked = 0;
+               hext_stream->link_substream = NULL;
                spin_unlock_irq(&bus->reg_lock);
                break;
 
@@ -437,11 +438,11 @@ EXPORT_SYMBOL_GPL(snd_hdac_ext_stream_spbcap_enable);
 /**
  * snd_hdac_ext_stream_set_spib - sets the spib value of a stream
  * @bus: HD-audio core bus
- * @stream: hdac_ext_stream
+ * @hext_stream: hdac_ext_stream
  * @value: spib value to set
  */
 int snd_hdac_ext_stream_set_spib(struct hdac_bus *bus,
-                                struct hdac_ext_stream *stream, u32 value)
+                                struct hdac_ext_stream *hext_stream, u32 value)
 {
 
        if (!bus->spbcap) {
@@ -449,7 +450,7 @@ int snd_hdac_ext_stream_set_spib(struct hdac_bus *bus,
                return -EINVAL;
        }
 
-       writel(value, stream->spib_addr);
+       writel(value, hext_stream->spib_addr);
 
        return 0;
 }
@@ -458,12 +459,12 @@ EXPORT_SYMBOL_GPL(snd_hdac_ext_stream_set_spib);
 /**
  * snd_hdac_ext_stream_get_spbmaxfifo - gets the spib value of a stream
  * @bus: HD-audio core bus
- * @stream: hdac_ext_stream
+ * @hext_stream: hdac_ext_stream
  *
  * Return maxfifo for the stream
  */
 int snd_hdac_ext_stream_get_spbmaxfifo(struct hdac_bus *bus,
-                                struct hdac_ext_stream *stream)
+                                struct hdac_ext_stream *hext_stream)
 {
 
        if (!bus->spbcap) {
@@ -471,27 +472,10 @@ int snd_hdac_ext_stream_get_spbmaxfifo(struct hdac_bus *bus,
                return -EINVAL;
        }
 
-       return readl(stream->fifo_addr);
+       return readl(hext_stream->fifo_addr);
 }
 EXPORT_SYMBOL_GPL(snd_hdac_ext_stream_get_spbmaxfifo);
 
-
-/**
- * snd_hdac_ext_stop_streams - stop all stream if running
- * @bus: HD-audio core bus
- */
-void snd_hdac_ext_stop_streams(struct hdac_bus *bus)
-{
-       struct hdac_stream *stream;
-
-       if (bus->chip_init) {
-               list_for_each_entry(stream, &bus->stream_list, list)
-                       snd_hdac_stream_stop(stream);
-               snd_hdac_bus_stop_chip(bus);
-       }
-}
-EXPORT_SYMBOL_GPL(snd_hdac_ext_stop_streams);
-
 /**
  * snd_hdac_ext_stream_drsm_enable - enable DMA resume for a stream
  * @bus: HD-audio core bus
@@ -520,11 +504,11 @@ EXPORT_SYMBOL_GPL(snd_hdac_ext_stream_drsm_enable);
 /**
  * snd_hdac_ext_stream_set_dpibr - sets the dpibr value of a stream
  * @bus: HD-audio core bus
- * @stream: hdac_ext_stream
+ * @hext_stream: hdac_ext_stream
  * @value: dpib value to set
  */
 int snd_hdac_ext_stream_set_dpibr(struct hdac_bus *bus,
-                                struct hdac_ext_stream *stream, u32 value)
+                                 struct hdac_ext_stream *hext_stream, u32 value)
 {
 
        if (!bus->drsmcap) {
@@ -532,7 +516,7 @@ int snd_hdac_ext_stream_set_dpibr(struct hdac_bus *bus,
                return -EINVAL;
        }
 
-       writel(value, stream->dpibr_addr);
+       writel(value, hext_stream->dpibr_addr);
 
        return 0;
 }
@@ -540,12 +524,12 @@ EXPORT_SYMBOL_GPL(snd_hdac_ext_stream_set_dpibr);
 
 /**
  * snd_hdac_ext_stream_set_lpib - sets the lpib value of a stream
- * @stream: hdac_ext_stream
+ * @hext_stream: hdac_ext_stream
  * @value: lpib value to set
  */
-int snd_hdac_ext_stream_set_lpib(struct hdac_ext_stream *stream, u32 value)
+int snd_hdac_ext_stream_set_lpib(struct hdac_ext_stream *hext_stream, u32 value)
 {
-       snd_hdac_stream_writel(&stream->hstream, SD_LPIB, value);
+       snd_hdac_stream_writel(&hext_stream->hstream, SD_LPIB, value);
 
        return 0;
 }
index 9867555..f358201 100644 (file)
@@ -142,6 +142,22 @@ void snd_hdac_stream_stop(struct hdac_stream *azx_dev)
 }
 EXPORT_SYMBOL_GPL(snd_hdac_stream_stop);
 
+/**
+ * snd_hdac_stop_streams_and_chip - stop all streams and chip if running
+ * @bus: HD-audio core bus
+ */
+void snd_hdac_stop_streams_and_chip(struct hdac_bus *bus)
+{
+       struct hdac_stream *stream;
+
+       if (bus->chip_init) {
+               list_for_each_entry(stream, &bus->stream_list, list)
+                       snd_hdac_stream_stop(stream);
+               snd_hdac_bus_stop_chip(bus);
+       }
+}
+EXPORT_SYMBOL_GPL(snd_hdac_stop_streams_and_chip);
+
 /**
  * snd_hdac_stream_reset - reset a stream
  * @azx_dev: HD-audio core stream to reset
@@ -534,17 +550,11 @@ static void azx_timecounter_init(struct hdac_stream *azx_dev,
        cc->mask = CLOCKSOURCE_MASK(32);
 
        /*
-        * Converting from 24 MHz to ns means applying a 125/3 factor.
-        * To avoid any saturation issues in intermediate operations,
-        * the 125 factor is applied first. The division is applied
-        * last after reading the timecounter value.
-        * Applying the 1/3 factor as part of the multiplication
-        * requires at least 20 bits for a decent precision, however
-        * overflows occur after about 4 hours or less, not a option.
+        * Calculate the optimal mult/shift values. The counter wraps
+        * around after ~178.9 seconds.
         */
-
-       cc->mult = 125; /* saturation after 195 years */
-       cc->shift = 0;
+       clocks_calc_mult_shift(&cc->mult, &cc->shift, 24000000,
+                              NSEC_PER_SEC, 178);
 
        nsec = 0; /* audio time is elapsed time since trigger */
        timecounter_init(tc, cc, nsec);
index b9ac9e9..c26229e 100644 (file)
@@ -252,6 +252,11 @@ static const struct config_entry config_table[] = {
                .flags = FLAG_SOF | FLAG_SOF_ONLY_IF_DMIC_OR_SOUNDWIRE,
                .device = 0x02c8,
        },
+       {
+               .flags = FLAG_SOF,
+               .device = 0x02c8,
+               .codec_hid = "ESSX8336",
+       },
 /* Cometlake-H */
        {
                .flags = FLAG_SOF,
@@ -276,6 +281,11 @@ static const struct config_entry config_table[] = {
                .flags = FLAG_SOF | FLAG_SOF_ONLY_IF_DMIC_OR_SOUNDWIRE,
                .device = 0x06c8,
        },
+               {
+               .flags = FLAG_SOF,
+               .device = 0x06c8,
+               .codec_hid = "ESSX8336",
+       },
 #endif
 
 /* Icelake */
@@ -299,6 +309,15 @@ static const struct config_entry config_table[] = {
        },
 #endif
 
+/* JasperLake */
+#if IS_ENABLED(CONFIG_SND_SOC_SOF_JASPERLAKE)
+       {
+               .flags = FLAG_SOF,
+               .device = 0x4dc8,
+               .codec_hid = "ESSX8336",
+       },
+#endif
+
 /* Tigerlake */
 #if IS_ENABLED(CONFIG_SND_SOC_SOF_TIGERLAKE)
        {
@@ -355,6 +374,14 @@ static const struct config_entry config_table[] = {
                .flags = FLAG_SOF | FLAG_SOF_ONLY_IF_DMIC_OR_SOUNDWIRE,
                .device = 0x51cc,
        },
+       {
+               .flags = FLAG_SOF | FLAG_SOF_ONLY_IF_DMIC_OR_SOUNDWIRE,
+               .device = 0x51cd,
+       },
+       {
+               .flags = FLAG_SOF | FLAG_SOF_ONLY_IF_DMIC_OR_SOUNDWIRE,
+               .device = 0x54c8,
+       },
 #endif
 
 };
@@ -384,7 +411,7 @@ static int snd_intel_dsp_check_dmic(struct pci_dev *pci)
 
        nhlt = intel_nhlt_init(&pci->dev);
        if (nhlt) {
-               if (intel_nhlt_get_dmic_geo(&pci->dev, nhlt))
+               if (intel_nhlt_has_endpoint_type(nhlt, NHLT_LINK_DMIC))
                        ret = 1;
                intel_nhlt_free(nhlt);
        }
index e223723..128476a 100644 (file)
@@ -110,3 +110,105 @@ int intel_nhlt_get_dmic_geo(struct device *dev, struct nhlt_acpi_table *nhlt)
        return dmic_geo;
 }
 EXPORT_SYMBOL_GPL(intel_nhlt_get_dmic_geo);
+
+bool intel_nhlt_has_endpoint_type(struct nhlt_acpi_table *nhlt, u8 link_type)
+{
+       struct nhlt_endpoint *epnt;
+       int i;
+
+       if (!nhlt)
+               return false;
+
+       epnt = (struct nhlt_endpoint *)nhlt->desc;
+       for (i = 0; i < nhlt->endpoint_count; i++) {
+               if (epnt->linktype == link_type)
+                       return true;
+
+               epnt = (struct nhlt_endpoint *)((u8 *)epnt + epnt->length);
+       }
+       return false;
+}
+EXPORT_SYMBOL(intel_nhlt_has_endpoint_type);
+
+static struct nhlt_specific_cfg *
+nhlt_get_specific_cfg(struct device *dev, struct nhlt_fmt *fmt, u8 num_ch,
+                     u32 rate, u8 vbps, u8 bps)
+{
+       struct nhlt_fmt_cfg *cfg = fmt->fmt_config;
+       struct wav_fmt *wfmt;
+       u16 _bps, _vbps;
+       int i;
+
+       dev_dbg(dev, "Endpoint format count=%d\n", fmt->fmt_count);
+
+       for (i = 0; i < fmt->fmt_count; i++) {
+               wfmt = &cfg->fmt_ext.fmt;
+               _bps = wfmt->bits_per_sample;
+               _vbps = cfg->fmt_ext.sample.valid_bits_per_sample;
+
+               dev_dbg(dev, "Endpoint format: ch=%d fmt=%d/%d rate=%d\n",
+                       wfmt->channels, _vbps, _bps, wfmt->samples_per_sec);
+
+               if (wfmt->channels == num_ch && wfmt->samples_per_sec == rate &&
+                   vbps == _vbps && bps == _bps)
+                       return &cfg->config;
+
+               cfg = (struct nhlt_fmt_cfg *)(cfg->config.caps + cfg->config.size);
+       }
+
+       return NULL;
+}
+
+static bool nhlt_check_ep_match(struct device *dev, struct nhlt_endpoint *epnt,
+                               u32 bus_id, u8 link_type, u8 dir, u8 dev_type)
+{
+       dev_dbg(dev, "Endpoint: vbus_id=%d link_type=%d dir=%d dev_type = %d\n",
+               epnt->virtual_bus_id, epnt->linktype,
+               epnt->direction, epnt->device_type);
+
+       if ((epnt->virtual_bus_id != bus_id) ||
+           (epnt->linktype != link_type) ||
+           (epnt->direction != dir))
+               return false;
+
+       /* link of type DMIC bypasses device_type check */
+       return epnt->linktype == NHLT_LINK_DMIC ||
+              epnt->device_type == dev_type;
+}
+
+struct nhlt_specific_cfg *
+intel_nhlt_get_endpoint_blob(struct device *dev, struct nhlt_acpi_table *nhlt,
+                            u32 bus_id, u8 link_type, u8 vbps, u8 bps,
+                            u8 num_ch, u32 rate, u8 dir, u8 dev_type)
+{
+       struct nhlt_specific_cfg *cfg;
+       struct nhlt_endpoint *epnt;
+       struct nhlt_fmt *fmt;
+       int i;
+
+       if (!nhlt)
+               return NULL;
+
+       dev_dbg(dev, "Looking for configuration:\n");
+       dev_dbg(dev, "  vbus_id=%d link_type=%d dir=%d, dev_type=%d\n",
+               bus_id, link_type, dir, dev_type);
+       dev_dbg(dev, "  ch=%d fmt=%d/%d rate=%d\n", num_ch, vbps, bps, rate);
+       dev_dbg(dev, "Endpoint count=%d\n", nhlt->endpoint_count);
+
+       epnt = (struct nhlt_endpoint *)nhlt->desc;
+
+       for (i = 0; i < nhlt->endpoint_count; i++) {
+               if (nhlt_check_ep_match(dev, epnt, bus_id, link_type, dir, dev_type)) {
+                       fmt = (struct nhlt_fmt *)(epnt->config.caps + epnt->config.size);
+
+                       cfg = nhlt_get_specific_cfg(dev, fmt, num_ch, rate, vbps, bps);
+                       if (cfg)
+                               return cfg;
+               }
+
+               epnt = (struct nhlt_endpoint *)((u8 *)epnt + epnt->length);
+       }
+
+       return NULL;
+}
+EXPORT_SYMBOL(intel_nhlt_get_endpoint_blob);
index c0123bc..b7758db 100644 (file)
@@ -132,8 +132,6 @@ static acpi_status sdw_intel_acpi_cb(acpi_handle handle, u32 level,
                return AE_NOT_FOUND;
        }
 
-       info->handle = handle;
-
        /*
         * On some Intel platforms, multiple children of the HDAS
         * device can be found, but only one of them is the SoundWire
@@ -144,6 +142,9 @@ static acpi_status sdw_intel_acpi_cb(acpi_handle handle, u32 level,
        if (FIELD_GET(GENMASK(31, 28), adr) != SDW_LINK_TYPE)
                return AE_OK; /* keep going */
 
+       /* found the correct SoundWire controller */
+       info->handle = handle;
+
        /* device found, stop namespace walk */
        return AE_CTRL_TERMINATE;
 }
@@ -164,8 +165,14 @@ int sdw_intel_acpi_scan(acpi_handle *parent_handle,
        acpi_status status;
 
        info->handle = NULL;
+       /*
+        * In the HDAS ACPI scope, 'SNDW' may be either the child of
+        * 'HDAS' or the grandchild of 'HDAS'. So let's go through
+        * the ACPI from 'HDAS' at max depth of 2 to find the 'SNDW'
+        * device.
+        */
        status = acpi_walk_namespace(ACPI_TYPE_DEVICE,
-                                    parent_handle, 1,
+                                    parent_handle, 2,
                                     sdw_intel_acpi_cb,
                                     NULL, info, NULL);
        if (ACPI_FAILURE(status) || info->handle == NULL)
index ff9480f..3e56c01 100644 (file)
@@ -24,8 +24,9 @@ void snd_gf1_mem_lock(struct snd_gf1_mem * alloc, int xup)
        }
 }
 
-static struct snd_gf1_mem_block *snd_gf1_mem_xalloc(struct snd_gf1_mem * alloc,
-                                              struct snd_gf1_mem_block * block)
+static struct snd_gf1_mem_block *
+snd_gf1_mem_xalloc(struct snd_gf1_mem *alloc, struct snd_gf1_mem_block *block,
+                  const char *name)
 {
        struct snd_gf1_mem_block *pblock, *nblock;
 
@@ -33,6 +34,12 @@ static struct snd_gf1_mem_block *snd_gf1_mem_xalloc(struct snd_gf1_mem * alloc,
        if (nblock == NULL)
                return NULL;
        *nblock = *block;
+       nblock->name = kstrdup(name, GFP_KERNEL);
+       if (!nblock->name) {
+               kfree(nblock);
+               return NULL;
+       }
+
        pblock = alloc->first;
        while (pblock) {
                if (pblock->ptr > nblock->ptr) {
@@ -44,7 +51,7 @@ static struct snd_gf1_mem_block *snd_gf1_mem_xalloc(struct snd_gf1_mem * alloc,
                        else
                                nblock->prev->next = nblock;
                        mutex_unlock(&alloc->memory_mutex);
-                       return NULL;
+                       return nblock;
                }
                pblock = pblock->next;
        }
@@ -198,8 +205,7 @@ struct snd_gf1_mem_block *snd_gf1_mem_alloc(struct snd_gf1_mem * alloc, int owne
        if (share_id != NULL)
                memcpy(&block.share_id, share_id, sizeof(block.share_id));
        block.owner = owner;
-       block.name = kstrdup(name, GFP_KERNEL);
-       nblock = snd_gf1_mem_xalloc(alloc, &block);
+       nblock = snd_gf1_mem_xalloc(alloc, &block, name);
        snd_gf1_mem_lock(alloc, 1);
        return nblock;
 }
@@ -236,14 +242,12 @@ int snd_gf1_mem_init(struct snd_gus_card * gus)
        if (gus->gf1.enh_mode) {
                block.ptr = 0;
                block.size = 1024;
-               block.name = kstrdup("InterWave LFOs", GFP_KERNEL);
-               if (snd_gf1_mem_xalloc(alloc, &block) == NULL)
+               if (!snd_gf1_mem_xalloc(alloc, &block, "InterWave LFOs"))
                        return -ENOMEM;
        }
        block.ptr = gus->gf1.default_voice_address;
        block.size = 4;
-       block.name = kstrdup("Voice default (NULL's)", GFP_KERNEL);
-       if (snd_gf1_mem_xalloc(alloc, &block) == NULL)
+       if (!snd_gf1_mem_xalloc(alloc, &block, "Voice default (NULL's)"))
                return -ENOMEM;
 #ifdef CONFIG_SND_DEBUG
        snd_card_ro_proc_new(gus->card, "gusmem", gus, snd_gf1_mem_info_read);
index 491de1a..5fee8e8 100644 (file)
@@ -231,7 +231,7 @@ static int set_spdif_rate(struct snd_ac97 *ac97, unsigned short rate)
  * If the codec doesn't support VAR, the rate must be 48000 (except
  * for SPDIF).
  *
- * The valid registers are AC97_PMC_MIC_ADC_RATE,
+ * The valid registers are AC97_PCM_MIC_ADC_RATE,
  * AC97_PCM_FRONT_DAC_RATE, AC97_PCM_LR_ADC_RATE.
  * AC97_PCM_SURR_DAC_RATE and AC97_PCM_LFE_DAC_RATE are accepted
  * if the codec supports them.
index ea20236..9a678b5 100644 (file)
@@ -3218,7 +3218,6 @@ static int snd_cmipci_probe(struct pci_dev *pci,
 {
        static int dev;
        struct snd_card *card;
-       struct cmipci *cm;
        int err;
 
        if (dev >= SNDRV_CARDS)
@@ -3229,10 +3228,9 @@ static int snd_cmipci_probe(struct pci_dev *pci,
        }
 
        err = snd_devm_card_new(&pci->dev, index[dev], id[dev], THIS_MODULE,
-                               sizeof(*cm), &card);
+                               sizeof(struct cmipci), &card);
        if (err < 0)
                return err;
-       cm = card->private_data;
        
        switch (pci->device) {
        case PCI_DEVICE_ID_CMEDIA_CM8738:
index da6e635..d074727 100644 (file)
 
 #define BLANK_SLOT             4094
 
-static int amixer_master(struct rsc *rsc)
+static void amixer_master(struct rsc *rsc)
 {
        rsc->conj = 0;
-       return rsc->idx = container_of(rsc, struct amixer, rsc)->idx[0];
+       rsc->idx = container_of(rsc, struct amixer, rsc)->idx[0];
 }
 
-static int amixer_next_conj(struct rsc *rsc)
+static void amixer_next_conj(struct rsc *rsc)
 {
        rsc->conj++;
-       return container_of(rsc, struct amixer, rsc)->idx[rsc->conj];
 }
 
 static int amixer_index(const struct rsc *rsc)
@@ -331,16 +330,15 @@ int amixer_mgr_destroy(struct amixer_mgr *amixer_mgr)
 
 /* SUM resource management */
 
-static int sum_master(struct rsc *rsc)
+static void sum_master(struct rsc *rsc)
 {
        rsc->conj = 0;
-       return rsc->idx = container_of(rsc, struct sum, rsc)->idx[0];
+       rsc->idx = container_of(rsc, struct sum, rsc)->idx[0];
 }
 
-static int sum_next_conj(struct rsc *rsc)
+static void sum_next_conj(struct rsc *rsc)
 {
        rsc->conj++;
-       return container_of(rsc, struct sum, rsc)->idx[rsc->conj];
 }
 
 static int sum_index(const struct rsc *rsc)
index f589da0..7fc7200 100644 (file)
@@ -51,12 +51,12 @@ static const struct daio_rsc_idx idx_20k2[NUM_DAIOTYP] = {
        [SPDIFIO] = {.left = 0x05, .right = 0x85},
 };
 
-static int daio_master(struct rsc *rsc)
+static void daio_master(struct rsc *rsc)
 {
        /* Actually, this is not the resource index of DAIO.
         * For DAO, it is the input mapper index. And, for DAI,
         * it is the output time-slot index. */
-       return rsc->conj = rsc->idx;
+       rsc->conj = rsc->idx;
 }
 
 static int daio_index(const struct rsc *rsc)
@@ -64,19 +64,19 @@ static int daio_index(const struct rsc *rsc)
        return rsc->conj;
 }
 
-static int daio_out_next_conj(struct rsc *rsc)
+static void daio_out_next_conj(struct rsc *rsc)
 {
-       return rsc->conj += 2;
+       rsc->conj += 2;
 }
 
-static int daio_in_next_conj_20k1(struct rsc *rsc)
+static void daio_in_next_conj_20k1(struct rsc *rsc)
 {
-       return rsc->conj += 0x200;
+       rsc->conj += 0x200;
 }
 
-static int daio_in_next_conj_20k2(struct rsc *rsc)
+static void daio_in_next_conj_20k2(struct rsc *rsc)
 {
-       return rsc->conj += 0x100;
+       rsc->conj += 0x100;
 }
 
 static const struct rsc_ops daio_out_rsc_ops = {
index 81ad269..be1d3e6 100644 (file)
@@ -109,18 +109,17 @@ static int audio_ring_slot(const struct rsc *rsc)
     return (rsc->conj << 4) + offset_in_audio_slot_block[rsc->type];
 }
 
-static int rsc_next_conj(struct rsc *rsc)
+static void rsc_next_conj(struct rsc *rsc)
 {
        unsigned int i;
        for (i = 0; (i < 8) && (!(rsc->msr & (0x1 << i))); )
                i++;
        rsc->conj += (AUDIO_SLOT_BLOCK_NUM >> i);
-       return rsc->conj;
 }
 
-static int rsc_master(struct rsc *rsc)
+static void rsc_master(struct rsc *rsc)
 {
-       return rsc->conj = rsc->idx;
+       rsc->conj = rsc->idx;
 }
 
 static const struct rsc_ops rsc_generic_ops = {
index fdbfd80..58553bd 100644 (file)
@@ -39,8 +39,8 @@ struct rsc {
 };
 
 struct rsc_ops {
-       int (*master)(struct rsc *rsc); /* Move to master resource */
-       int (*next_conj)(struct rsc *rsc); /* Move to next conjugate resource */
+       void (*master)(struct rsc *rsc); /* Move to master resource */
+       void (*next_conj)(struct rsc *rsc); /* Move to next conjugate resource */
        int (*index)(const struct rsc *rsc); /* Return the index of resource */
        /* Return the output slot number */
        int (*output_slot)(const struct rsc *rsc);
index bd4697b..4a94b47 100644 (file)
@@ -590,16 +590,15 @@ int src_mgr_destroy(struct src_mgr *src_mgr)
 
 /* SRCIMP resource manager operations */
 
-static int srcimp_master(struct rsc *rsc)
+static void srcimp_master(struct rsc *rsc)
 {
        rsc->conj = 0;
-       return rsc->idx = container_of(rsc, struct srcimp, rsc)->idx[0];
+       rsc->idx = container_of(rsc, struct srcimp, rsc)->idx[0];
 }
 
-static int srcimp_next_conj(struct rsc *rsc)
+static void srcimp_next_conj(struct rsc *rsc)
 {
        rsc->conj++;
-       return container_of(rsc, struct srcimp, rsc)->idx[rsc->conj];
 }
 
 static int srcimp_index(const struct rsc *rsc)
index 4a85447..82c492b 100644 (file)
@@ -92,14 +92,10 @@ static int compare_input_type(const void *ap, const void *bp)
  */
 static void reorder_outputs(unsigned int nums, hda_nid_t *pins)
 {
-       hda_nid_t nid;
-
        switch (nums) {
        case 3:
        case 4:
-               nid = pins[1];
-               pins[1] = pins[2];
-               pins[2] = nid;
+               swap(pins[1], pins[2]);
                break;
        }
 }
index 1c8bffc..c572fb5 100644 (file)
@@ -14,6 +14,7 @@
 #include <sound/core.h>
 #include <sound/hda_codec.h>
 #include "hda_local.h"
+#include "hda_jack.h"
 
 /*
  * find a matching codec id
@@ -156,6 +157,12 @@ static int hda_codec_driver_remove(struct device *dev)
                return codec->bus->core.ext_ops->hdev_detach(&codec->core);
        }
 
+       refcount_dec(&codec->pcm_ref);
+       snd_hda_codec_disconnect_pcms(codec);
+       snd_hda_jack_tbl_disconnect(codec);
+       wait_event(codec->remove_sleep, !refcount_read(&codec->pcm_ref));
+       snd_power_sync_ref(codec->bus->card);
+
        if (codec->patch_ops.free)
                codec->patch_ops.free(codec);
        snd_hda_codec_cleanup_for_unbind(codec);
index 0c4a337..7016b48 100644 (file)
@@ -703,20 +703,10 @@ get_hda_cvt_setup(struct hda_codec *codec, hda_nid_t nid)
 /*
  * PCM device
  */
-static void release_pcm(struct kref *kref)
-{
-       struct hda_pcm *pcm = container_of(kref, struct hda_pcm, kref);
-
-       if (pcm->pcm)
-               snd_device_free(pcm->codec->card, pcm->pcm);
-       clear_bit(pcm->device, pcm->codec->bus->pcm_dev_bits);
-       kfree(pcm->name);
-       kfree(pcm);
-}
-
 void snd_hda_codec_pcm_put(struct hda_pcm *pcm)
 {
-       kref_put(&pcm->kref, release_pcm);
+       if (refcount_dec_and_test(&pcm->codec->pcm_ref))
+               wake_up(&pcm->codec->remove_sleep);
 }
 EXPORT_SYMBOL_GPL(snd_hda_codec_pcm_put);
 
@@ -731,7 +721,6 @@ struct hda_pcm *snd_hda_codec_pcm_new(struct hda_codec *codec,
                return NULL;
 
        pcm->codec = codec;
-       kref_init(&pcm->kref);
        va_start(args, fmt);
        pcm->name = kvasprintf(GFP_KERNEL, fmt, args);
        va_end(args);
@@ -741,6 +730,7 @@ struct hda_pcm *snd_hda_codec_pcm_new(struct hda_codec *codec,
        }
 
        list_add_tail(&pcm->list, &codec->pcm_list_head);
+       refcount_inc(&codec->pcm_ref);
        return pcm;
 }
 EXPORT_SYMBOL_GPL(snd_hda_codec_pcm_new);
@@ -748,15 +738,31 @@ EXPORT_SYMBOL_GPL(snd_hda_codec_pcm_new);
 /*
  * codec destructor
  */
+void snd_hda_codec_disconnect_pcms(struct hda_codec *codec)
+{
+       struct hda_pcm *pcm;
+
+       list_for_each_entry(pcm, &codec->pcm_list_head, list) {
+               if (pcm->disconnected)
+                       continue;
+               if (pcm->pcm)
+                       snd_device_disconnect(codec->card, pcm->pcm);
+               snd_hda_codec_pcm_put(pcm);
+               pcm->disconnected = 1;
+       }
+}
+
 static void codec_release_pcms(struct hda_codec *codec)
 {
        struct hda_pcm *pcm, *n;
 
        list_for_each_entry_safe(pcm, n, &codec->pcm_list_head, list) {
-               list_del_init(&pcm->list);
+               list_del(&pcm->list);
                if (pcm->pcm)
-                       snd_device_disconnect(codec->card, pcm->pcm);
-               snd_hda_codec_pcm_put(pcm);
+                       snd_device_free(pcm->codec->card, pcm->pcm);
+               clear_bit(pcm->device, pcm->codec->bus->pcm_dev_bits);
+               kfree(pcm->name);
+               kfree(pcm);
        }
 }
 
@@ -769,6 +775,7 @@ void snd_hda_codec_cleanup_for_unbind(struct hda_codec *codec)
                codec->registered = 0;
        }
 
+       snd_hda_codec_disconnect_pcms(codec);
        cancel_delayed_work_sync(&codec->jackpoll_work);
        if (!codec->in_freeing)
                snd_hda_ctls_clear(codec);
@@ -792,6 +799,7 @@ void snd_hda_codec_cleanup_for_unbind(struct hda_codec *codec)
        remove_conn_list(codec);
        snd_hdac_regmap_exit(&codec->core);
        codec->configured = 0;
+       refcount_set(&codec->pcm_ref, 1); /* reset refcount */
 }
 EXPORT_SYMBOL_GPL(snd_hda_codec_cleanup_for_unbind);
 
@@ -958,6 +966,8 @@ int snd_hda_codec_device_new(struct hda_bus *bus, struct snd_card *card,
        snd_array_init(&codec->verbs, sizeof(struct hda_verb *), 8);
        INIT_LIST_HEAD(&codec->conn_list);
        INIT_LIST_HEAD(&codec->pcm_list_head);
+       refcount_set(&codec->pcm_ref, 1);
+       init_waitqueue_head(&codec->remove_sleep);
 
        INIT_DELAYED_WORK(&codec->jackpoll_work, hda_jackpoll_work);
        codec->depop_delay = -1;
@@ -1727,8 +1737,11 @@ void snd_hda_ctls_clear(struct hda_codec *codec)
 {
        int i;
        struct hda_nid_item *items = codec->mixers.list;
+
+       down_write(&codec->card->controls_rwsem);
        for (i = 0; i < codec->mixers.used; i++)
                snd_ctl_remove(codec->card, items[i].kctl);
+       up_write(&codec->card->controls_rwsem);
        snd_array_free(&codec->mixers);
        snd_array_free(&codec->nids);
 }
index 930ae40..75dcb14 100644 (file)
@@ -504,7 +504,6 @@ static int azx_get_time_info(struct snd_pcm_substream *substream,
                snd_pcm_gettime(substream->runtime, system_ts);
 
                nsec = timecounter_read(&azx_dev->core.tc);
-               nsec = div_u64(nsec, 3); /* can be optimized */
                if (audio_tstamp_config->report_delay)
                        nsec = azx_adjust_codec_delay(substream, nsec);
 
index c43bd0f..8e1bc8e 100644 (file)
@@ -183,7 +183,7 @@ struct hda_gen_spec {
        struct automic_entry am_entry[MAX_AUTO_MIC_PINS];
 
        /* for pin sensing */
-       /* current status; set in hda_geneic.c */
+       /* current status; set in hda_generic.c */
        unsigned int hp_jack_present:1;
        unsigned int line_jack_present:1;
        unsigned int speaker_muted:1; /* current status of speaker mute */
index fe51163..4b0338c 100644 (file)
@@ -335,7 +335,10 @@ enum {
                                        ((pci)->device == 0x0c0c) || \
                                        ((pci)->device == 0x0d0c) || \
                                        ((pci)->device == 0x160c) || \
-                                       ((pci)->device == 0x490d))
+                                       ((pci)->device == 0x490d) || \
+                                       ((pci)->device == 0x4f90) || \
+                                       ((pci)->device == 0x4f91) || \
+                                       ((pci)->device == 0x4f92))
 
 #define IS_BXT(pci) ((pci)->vendor == 0x8086 && (pci)->device == 0x5a98)
 
@@ -1347,8 +1350,12 @@ static void azx_free(struct azx *chip)
        if (hda->freed)
                return;
 
-       if (azx_has_pm_runtime(chip) && chip->running)
+       if (azx_has_pm_runtime(chip) && chip->running) {
                pm_runtime_get_noresume(&pci->dev);
+               pm_runtime_forbid(&pci->dev);
+               pm_runtime_dont_use_autosuspend(&pci->dev);
+       }
+
        chip->running = 0;
 
        azx_del_card_list(chip);
@@ -2473,15 +2480,27 @@ static const struct pci_device_id azx_ids[] = {
        /* DG1 */
        { PCI_DEVICE(0x8086, 0x490d),
          .driver_data = AZX_DRIVER_SKL | AZX_DCAPS_INTEL_SKYLAKE},
+       /* DG2 */
+       { PCI_DEVICE(0x8086, 0x4f90),
+         .driver_data = AZX_DRIVER_SKL | AZX_DCAPS_INTEL_SKYLAKE},
+       { PCI_DEVICE(0x8086, 0x4f91),
+         .driver_data = AZX_DRIVER_SKL | AZX_DCAPS_INTEL_SKYLAKE},
+       { PCI_DEVICE(0x8086, 0x4f92),
+         .driver_data = AZX_DRIVER_SKL | AZX_DCAPS_INTEL_SKYLAKE},
        /* Alderlake-S */
        { PCI_DEVICE(0x8086, 0x7ad0),
          .driver_data = AZX_DRIVER_SKL | AZX_DCAPS_INTEL_SKYLAKE},
        /* Alderlake-P */
        { PCI_DEVICE(0x8086, 0x51c8),
          .driver_data = AZX_DRIVER_SKL | AZX_DCAPS_INTEL_SKYLAKE},
+       { PCI_DEVICE(0x8086, 0x51cd),
+         .driver_data = AZX_DRIVER_SKL | AZX_DCAPS_INTEL_SKYLAKE},
        /* Alderlake-M */
        { PCI_DEVICE(0x8086, 0x51cc),
          .driver_data = AZX_DRIVER_SKL | AZX_DCAPS_INTEL_SKYLAKE},
+       /* Alderlake-N */
+       { PCI_DEVICE(0x8086, 0x54c8),
+         .driver_data = AZX_DRIVER_SKL | AZX_DCAPS_INTEL_SKYLAKE},
        /* Elkhart Lake */
        { PCI_DEVICE(0x8086, 0x4b55),
          .driver_data = AZX_DRIVER_SKL | AZX_DCAPS_INTEL_SKYLAKE},
index f29975e..7d7786d 100644 (file)
@@ -158,6 +158,17 @@ snd_hda_jack_tbl_new(struct hda_codec *codec, hda_nid_t nid, int dev_id)
        return jack;
 }
 
+void snd_hda_jack_tbl_disconnect(struct hda_codec *codec)
+{
+       struct hda_jack_tbl *jack = codec->jacktbl.list;
+       int i;
+
+       for (i = 0; i < codec->jacktbl.used; i++, jack++) {
+               if (!codec->bus->shutdown && jack->jack)
+                       snd_device_disconnect(codec->card, jack->jack);
+       }
+}
+
 void snd_hda_jack_tbl_clear(struct hda_codec *codec)
 {
        struct hda_jack_tbl *jack = codec->jacktbl.list;
index 2abf7aa..ff7d289 100644 (file)
@@ -69,6 +69,7 @@ struct hda_jack_tbl *
 snd_hda_jack_tbl_get_from_tag(struct hda_codec *codec,
                              unsigned char tag, int dev_id);
 
+void snd_hda_jack_tbl_disconnect(struct hda_codec *codec);
 void snd_hda_jack_tbl_clear(struct hda_codec *codec);
 
 void snd_hda_jack_set_dirty_all(struct hda_codec *codec);
index ea8ab8b..8621f57 100644 (file)
@@ -137,6 +137,7 @@ int __snd_hda_add_vmaster(struct hda_codec *codec, char *name,
 int snd_hda_codec_reset(struct hda_codec *codec);
 void snd_hda_codec_register(struct hda_codec *codec);
 void snd_hda_codec_cleanup_for_unbind(struct hda_codec *codec);
+void snd_hda_codec_disconnect_pcms(struct hda_codec *codec);
 
 #define snd_hda_regmap_sync(codec)     snd_hdac_regmap_sync(&(codec)->core)
 
@@ -438,6 +439,15 @@ int snd_hda_codec_set_pin_target(struct hda_codec *codec, hda_nid_t nid,
 #define for_each_hda_codec_node(nid, codec) \
        for ((nid) = (codec)->core.start_nid; (nid) < (codec)->core.end_nid; (nid)++)
 
+/* Set the codec power_state flag to indicate to allow unsol event handling;
+ * see hda_codec_unsol_event() in hda_bind.c.  Calling this might confuse the
+ * state tracking, so use with care.
+ */
+static inline void snd_hda_codec_allow_unsol_events(struct hda_codec *codec)
+{
+       codec->core.dev.power.power_state = PMSG_ON;
+}
+
 /*
  * get widget capabilities
  */
index ea70039..773f490 100644 (file)
  */
 #define TEGRA194_NUM_SDO_LINES   4
 
+struct hda_tegra_soc {
+       bool has_hda2codec_2x_reset;
+};
+
 struct hda_tegra {
        struct azx chip;
        struct device *dev;
-       struct reset_control *reset;
+       struct reset_control_bulk_data resets[3];
        struct clk_bulk_data clocks[3];
+       unsigned int nresets;
        unsigned int nclocks;
        void __iomem *regs;
        struct work_struct probe_work;
+       const struct hda_tegra_soc *soc;
 };
 
 #ifdef CONFIG_PM
@@ -170,7 +176,7 @@ static int __maybe_unused hda_tegra_runtime_resume(struct device *dev)
        int rc;
 
        if (!chip->running) {
-               rc = reset_control_assert(hda->reset);
+               rc = reset_control_bulk_assert(hda->nresets, hda->resets);
                if (rc)
                        return rc;
        }
@@ -187,7 +193,7 @@ static int __maybe_unused hda_tegra_runtime_resume(struct device *dev)
        } else {
                usleep_range(10, 100);
 
-               rc = reset_control_deassert(hda->reset);
+               rc = reset_control_bulk_deassert(hda->nresets, hda->resets);
                if (rc)
                        return rc;
        }
@@ -427,9 +433,17 @@ static int hda_tegra_create(struct snd_card *card,
        return 0;
 }
 
+static const struct hda_tegra_soc tegra30_data = {
+       .has_hda2codec_2x_reset = true,
+};
+
+static const struct hda_tegra_soc tegra194_data = {
+       .has_hda2codec_2x_reset = false,
+};
+
 static const struct of_device_id hda_tegra_match[] = {
-       { .compatible = "nvidia,tegra30-hda" },
-       { .compatible = "nvidia,tegra194-hda" },
+       { .compatible = "nvidia,tegra30-hda", .data = &tegra30_data },
+       { .compatible = "nvidia,tegra194-hda", .data = &tegra194_data },
        {},
 };
 MODULE_DEVICE_TABLE(of, hda_tegra_match);
@@ -449,6 +463,8 @@ static int hda_tegra_probe(struct platform_device *pdev)
        hda->dev = &pdev->dev;
        chip = &hda->chip;
 
+       hda->soc = of_device_get_match_data(&pdev->dev);
+
        err = snd_card_new(&pdev->dev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1,
                           THIS_MODULE, 0, &card);
        if (err < 0) {
@@ -456,11 +472,20 @@ static int hda_tegra_probe(struct platform_device *pdev)
                return err;
        }
 
-       hda->reset = devm_reset_control_array_get_exclusive(&pdev->dev);
-       if (IS_ERR(hda->reset)) {
-               err = PTR_ERR(hda->reset);
+       hda->resets[hda->nresets++].id = "hda";
+       hda->resets[hda->nresets++].id = "hda2hdmi";
+       /*
+        * "hda2codec_2x" reset is not present on Tegra194. Though DT would
+        * be updated to reflect this, but to have backward compatibility
+        * below is necessary.
+        */
+       if (hda->soc->has_hda2codec_2x_reset)
+               hda->resets[hda->nresets++].id = "hda2codec_2x";
+
+       err = devm_reset_control_bulk_get_exclusive(&pdev->dev, hda->nresets,
+                                                   hda->resets);
+       if (err)
                goto out_free;
-       }
 
        hda->clocks[hda->nclocks++].id = "hda";
        hda->clocks[hda->nclocks++].id = "hda2hdmi";
index 0fb0a42..df0b452 100644 (file)
@@ -252,6 +252,7 @@ struct sub_codec cs8409_cs42l42_codec = {
        .init_seq_num = ARRAY_SIZE(cs42l42_init_reg_seq),
        .hp_jack_in = 0,
        .mic_jack_in = 0,
+       .force_status_change = 1,
        .paged = 1,
        .suspended = 1,
        .no_type_dect = 0,
@@ -443,6 +444,7 @@ struct sub_codec dolphin_cs42l42_0 = {
        .init_seq_num = ARRAY_SIZE(dolphin_c0_init_reg_seq),
        .hp_jack_in = 0,
        .mic_jack_in = 0,
+       .force_status_change = 1,
        .paged = 1,
        .suspended = 1,
        .no_type_dect = 0,
@@ -456,6 +458,7 @@ struct sub_codec dolphin_cs42l42_1 = {
        .init_seq_num = ARRAY_SIZE(dolphin_c1_init_reg_seq),
        .hp_jack_in = 0,
        .mic_jack_in = 0,
+       .force_status_change = 1,
        .paged = 1,
        .suspended = 1,
        .no_type_dect = 1,
index 31ff11a..aff2b5a 100644 (file)
@@ -628,15 +628,17 @@ static void cs42l42_run_jack_detect(struct sub_codec *cs42l42)
        cs8409_i2c_write(cs42l42, 0x1b74, 0x07);
        cs8409_i2c_write(cs42l42, 0x131b, 0xFD);
        cs8409_i2c_write(cs42l42, 0x1120, 0x80);
-       /* Wait ~100us*/
-       usleep_range(100, 200);
+       /* Wait ~20ms*/
+       usleep_range(20000, 25000);
        cs8409_i2c_write(cs42l42, 0x111f, 0x77);
        cs8409_i2c_write(cs42l42, 0x1120, 0xc0);
 }
 
 static int cs42l42_handle_tip_sense(struct sub_codec *cs42l42, unsigned int reg_ts_status)
 {
-       int status_changed = 0;
+       int status_changed = cs42l42->force_status_change;
+
+       cs42l42->force_status_change = 0;
 
        /* TIP_SENSE INSERT/REMOVE */
        switch (reg_ts_status) {
@@ -750,6 +752,11 @@ static void cs42l42_resume(struct sub_codec *cs42l42)
        if (cs42l42->full_scale_vol)
                cs8409_i2c_write(cs42l42, 0x2001, 0x01);
 
+       /* we have to explicitly allow unsol event handling even during the
+        * resume phase so that the jack event is processed properly
+        */
+       snd_hda_codec_allow_unsol_events(cs42l42->codec);
+
        cs42l42_enable_jack_detect(cs42l42);
 }
 
@@ -786,6 +793,7 @@ static void cs42l42_suspend(struct sub_codec *cs42l42)
        cs42l42->last_page = 0;
        cs42l42->hp_jack_in = 0;
        cs42l42->mic_jack_in = 0;
+       cs42l42->force_status_change = 1;
 
        /* Put CS42L42 into Reset */
        gpio_data = snd_hda_codec_read(codec, CS8409_PIN_AFG, 0, AC_VERB_GET_GPIO_DATA, 0);
index ade2b83..d0b725c 100644 (file)
@@ -305,6 +305,7 @@ struct sub_codec {
 
        unsigned int hp_jack_in:1;
        unsigned int mic_jack_in:1;
+       unsigned int force_status_change:1;
        unsigned int suspended:1;
        unsigned int paged:1;
        unsigned int last_page;
index 65d2c55..92df4f2 100644 (file)
@@ -1535,7 +1535,7 @@ static void update_eld(struct hda_codec *codec,
                }
        }
 
-       if (!eld->eld_valid || eld->eld_size <= 0) {
+       if (!eld->eld_valid || eld->eld_size <= 0 || eld->info.sad_count <= 0) {
                eld->eld_valid = false;
                eld->eld_size = 0;
        }
@@ -2947,7 +2947,8 @@ static int parse_intel_hdmi(struct hda_codec *codec)
 
 /* Intel Haswell and onwards; audio component with eld notifier */
 static int intel_hsw_common_init(struct hda_codec *codec, hda_nid_t vendor_nid,
-                                const int *port_map, int port_num, int dev_num)
+                                const int *port_map, int port_num, int dev_num,
+                                bool send_silent_stream)
 {
        struct hdmi_spec *spec;
        int err;
@@ -2980,7 +2981,7 @@ static int intel_hsw_common_init(struct hda_codec *codec, hda_nid_t vendor_nid,
         * Enable silent stream feature, if it is enabled via
         * module param or Kconfig option
         */
-       if (enable_silent_stream)
+       if (send_silent_stream)
                spec->send_silent_stream = true;
 
        return parse_intel_hdmi(codec);
@@ -2988,12 +2989,18 @@ static int intel_hsw_common_init(struct hda_codec *codec, hda_nid_t vendor_nid,
 
 static int patch_i915_hsw_hdmi(struct hda_codec *codec)
 {
-       return intel_hsw_common_init(codec, 0x08, NULL, 0, 3);
+       return intel_hsw_common_init(codec, 0x08, NULL, 0, 3,
+                                    enable_silent_stream);
 }
 
 static int patch_i915_glk_hdmi(struct hda_codec *codec)
 {
-       return intel_hsw_common_init(codec, 0x0b, NULL, 0, 3);
+       /*
+        * Silent stream calls audio component .get_power() from
+        * .pin_eld_notify(). On GLK this will deadlock in i915 due
+        * to the audio vs. CDCLK workaround.
+        */
+       return intel_hsw_common_init(codec, 0x0b, NULL, 0, 3, false);
 }
 
 static int patch_i915_icl_hdmi(struct hda_codec *codec)
@@ -3004,7 +3011,8 @@ static int patch_i915_icl_hdmi(struct hda_codec *codec)
         */
        static const int map[] = {0x0, 0x4, 0x6, 0x8, 0xa, 0xb};
 
-       return intel_hsw_common_init(codec, 0x02, map, ARRAY_SIZE(map), 3);
+       return intel_hsw_common_init(codec, 0x02, map, ARRAY_SIZE(map), 3,
+                                    enable_silent_stream);
 }
 
 static int patch_i915_tgl_hdmi(struct hda_codec *codec)
@@ -3016,7 +3024,8 @@ static int patch_i915_tgl_hdmi(struct hda_codec *codec)
        static const int map[] = {0x4, 0x6, 0x8, 0xa, 0xb, 0xc, 0xd, 0xe, 0xf};
        int ret;
 
-       ret = intel_hsw_common_init(codec, 0x02, map, ARRAY_SIZE(map), 4);
+       ret = intel_hsw_common_init(codec, 0x02, map, ARRAY_SIZE(map), 4,
+                                   enable_silent_stream);
        if (!ret) {
                struct hdmi_spec *spec = codec->spec;
 
@@ -4380,10 +4389,11 @@ HDA_CODEC_ENTRY(0x8086280f, "Icelake HDMI",     patch_i915_icl_hdmi),
 HDA_CODEC_ENTRY(0x80862812, "Tigerlake HDMI",  patch_i915_tgl_hdmi),
 HDA_CODEC_ENTRY(0x80862814, "DG1 HDMI",        patch_i915_tgl_hdmi),
 HDA_CODEC_ENTRY(0x80862815, "Alderlake HDMI",  patch_i915_tgl_hdmi),
-HDA_CODEC_ENTRY(0x8086281c, "Alderlake-P HDMI", patch_i915_tgl_hdmi),
 HDA_CODEC_ENTRY(0x80862816, "Rocketlake HDMI", patch_i915_tgl_hdmi),
+HDA_CODEC_ENTRY(0x80862819, "DG2 HDMI",        patch_i915_tgl_hdmi),
 HDA_CODEC_ENTRY(0x8086281a, "Jasperlake HDMI", patch_i915_icl_hdmi),
 HDA_CODEC_ENTRY(0x8086281b, "Elkhartlake HDMI",        patch_i915_icl_hdmi),
+HDA_CODEC_ENTRY(0x8086281c, "Alderlake-P HDMI", patch_i915_tgl_hdmi),
 HDA_CODEC_ENTRY(0x80862880, "CedarTrail HDMI", patch_generic_hdmi),
 HDA_CODEC_ENTRY(0x80862882, "Valleyview2 HDMI",        patch_i915_byt_hdmi),
 HDA_CODEC_ENTRY(0x80862883, "Braswell HDMI",   patch_i915_byt_hdmi),
index 2f1727f..98736f2 100644 (file)
@@ -1924,6 +1924,7 @@ enum {
        ALC887_FIXUP_ASUS_BASS,
        ALC887_FIXUP_BASS_CHMAP,
        ALC1220_FIXUP_GB_DUAL_CODECS,
+       ALC1220_FIXUP_GB_X570,
        ALC1220_FIXUP_CLEVO_P950,
        ALC1220_FIXUP_CLEVO_PB51ED,
        ALC1220_FIXUP_CLEVO_PB51ED_PINS,
@@ -2113,6 +2114,29 @@ static void alc1220_fixup_gb_dual_codecs(struct hda_codec *codec,
        }
 }
 
+static void alc1220_fixup_gb_x570(struct hda_codec *codec,
+                                    const struct hda_fixup *fix,
+                                    int action)
+{
+       static const hda_nid_t conn1[] = { 0x0c };
+       static const struct coef_fw gb_x570_coefs[] = {
+               WRITE_COEF(0x1a, 0x01c1),
+               WRITE_COEF(0x1b, 0x0202),
+               WRITE_COEF(0x43, 0x3005),
+               {}
+       };
+
+       switch (action) {
+       case HDA_FIXUP_ACT_PRE_PROBE:
+               snd_hda_override_conn_list(codec, 0x14, ARRAY_SIZE(conn1), conn1);
+               snd_hda_override_conn_list(codec, 0x1b, ARRAY_SIZE(conn1), conn1);
+               break;
+       case HDA_FIXUP_ACT_INIT:
+               alc_process_coef_fw(codec, gb_x570_coefs);
+               break;
+       }
+}
+
 static void alc1220_fixup_clevo_p950(struct hda_codec *codec,
                                     const struct hda_fixup *fix,
                                     int action)
@@ -2415,6 +2439,10 @@ static const struct hda_fixup alc882_fixups[] = {
                .type = HDA_FIXUP_FUNC,
                .v.func = alc1220_fixup_gb_dual_codecs,
        },
+       [ALC1220_FIXUP_GB_X570] = {
+               .type = HDA_FIXUP_FUNC,
+               .v.func = alc1220_fixup_gb_x570,
+       },
        [ALC1220_FIXUP_CLEVO_P950] = {
                .type = HDA_FIXUP_FUNC,
                .v.func = alc1220_fixup_clevo_p950,
@@ -2517,7 +2545,7 @@ static const struct snd_pci_quirk alc882_fixup_tbl[] = {
        SND_PCI_QUIRK(0x13fe, 0x1009, "Advantech MIT-W101", ALC886_FIXUP_EAPD),
        SND_PCI_QUIRK(0x1458, 0xa002, "Gigabyte EP45-DS3/Z87X-UD3H", ALC889_FIXUP_FRONT_HP_NO_PRESENCE),
        SND_PCI_QUIRK(0x1458, 0xa0b8, "Gigabyte AZ370-Gaming", ALC1220_FIXUP_GB_DUAL_CODECS),
-       SND_PCI_QUIRK(0x1458, 0xa0cd, "Gigabyte X570 Aorus Master", ALC1220_FIXUP_CLEVO_P950),
+       SND_PCI_QUIRK(0x1458, 0xa0cd, "Gigabyte X570 Aorus Master", ALC1220_FIXUP_GB_X570),
        SND_PCI_QUIRK(0x1458, 0xa0ce, "Gigabyte X570 Aorus Xtreme", ALC1220_FIXUP_CLEVO_P950),
        SND_PCI_QUIRK(0x1462, 0x11f7, "MSI-GE63", ALC1220_FIXUP_CLEVO_P950),
        SND_PCI_QUIRK(0x1462, 0x1228, "MSI-GP63", ALC1220_FIXUP_CLEVO_P950),
@@ -6503,22 +6531,64 @@ static void alc287_fixup_legion_15imhg05_speakers(struct hda_codec *codec,
 /* for alc285_fixup_ideapad_s740_coef() */
 #include "ideapad_s740_helper.c"
 
-static void alc256_fixup_tongfang_reset_persistent_settings(struct hda_codec *codec,
-                                                           const struct hda_fixup *fix,
-                                                           int action)
+static const struct coef_fw alc256_fixup_set_coef_defaults_coefs[] = {
+       WRITE_COEF(0x10, 0x0020), WRITE_COEF(0x24, 0x0000),
+       WRITE_COEF(0x26, 0x0000), WRITE_COEF(0x29, 0x3000),
+       WRITE_COEF(0x37, 0xfe05), WRITE_COEF(0x45, 0x5089),
+       {}
+};
+
+static void alc256_fixup_set_coef_defaults(struct hda_codec *codec,
+                                          const struct hda_fixup *fix,
+                                          int action)
 {
        /*
-       * A certain other OS sets these coeffs to different values. On at least one TongFang
-       * barebone these settings might survive even a cold reboot. So to restore a clean slate the
-       * values are explicitly reset to default here. Without this, the external microphone is
-       * always in a plugged-in state, while the internal microphone is always in an unplugged
-       * state, breaking the ability to use the internal microphone.
-       */
-       alc_write_coef_idx(codec, 0x24, 0x0000);
-       alc_write_coef_idx(codec, 0x26, 0x0000);
-       alc_write_coef_idx(codec, 0x29, 0x3000);
-       alc_write_coef_idx(codec, 0x37, 0xfe05);
-       alc_write_coef_idx(codec, 0x45, 0x5089);
+        * A certain other OS sets these coeffs to different values. On at least
+        * one TongFang barebone these settings might survive even a cold
+        * reboot. So to restore a clean slate the values are explicitly reset
+        * to default here. Without this, the external microphone is always in a
+        * plugged-in state, while the internal microphone is always in an
+        * unplugged state, breaking the ability to use the internal microphone.
+        */
+       alc_process_coef_fw(codec, alc256_fixup_set_coef_defaults_coefs);
+}
+
+static const struct coef_fw alc233_fixup_no_audio_jack_coefs[] = {
+       WRITE_COEF(0x1a, 0x9003), WRITE_COEF(0x1b, 0x0e2b), WRITE_COEF(0x37, 0xfe06),
+       WRITE_COEF(0x38, 0x4981), WRITE_COEF(0x45, 0xd489), WRITE_COEF(0x46, 0x0074),
+       WRITE_COEF(0x49, 0x0149),
+       {}
+};
+
+static void alc233_fixup_no_audio_jack(struct hda_codec *codec,
+                                      const struct hda_fixup *fix,
+                                      int action)
+{
+       /*
+        * The audio jack input and output is not detected on the ASRock NUC Box
+        * 1100 series when cold booting without this fix. Warm rebooting from a
+        * certain other OS makes the audio functional, as COEF settings are
+        * preserved in this case. This fix sets these altered COEF values as
+        * the default.
+        */
+       alc_process_coef_fw(codec, alc233_fixup_no_audio_jack_coefs);
+}
+
+static void alc256_fixup_mic_no_presence_and_resume(struct hda_codec *codec,
+                                                   const struct hda_fixup *fix,
+                                                   int action)
+{
+       /*
+        * The Clevo NJ51CU comes either with the ALC293 or the ALC256 codec,
+        * but uses the 0x8686 subproduct id in both cases. The ALC256 codec
+        * needs an additional quirk for sound working after suspend and resume.
+        */
+       if (codec->core.vendor_id == 0x10ec0256) {
+               alc_update_coef_idx(codec, 0x10, 1<<9, 0);
+               snd_hda_codec_set_pincfg(codec, 0x19, 0x04a11120);
+       } else {
+               snd_hda_codec_set_pincfg(codec, 0x1a, 0x04a1113c);
+       }
 }
 
 enum {
@@ -6738,8 +6808,10 @@ enum {
        ALC287_FIXUP_LEGION_15IMHG05_AUTOMUTE,
        ALC287_FIXUP_YOGA7_14ITL_SPEAKERS,
        ALC287_FIXUP_13S_GEN2_SPEAKERS,
-       ALC256_FIXUP_TONGFANG_RESET_PERSISTENT_SETTINGS,
+       ALC256_FIXUP_SET_COEF_DEFAULTS,
        ALC256_FIXUP_SYSTEM76_MIC_NO_PRESENCE,
+       ALC233_FIXUP_NO_AUDIO_JACK,
+       ALC256_FIXUP_MIC_NO_PRESENCE_AND_RESUME,
 };
 
 static const struct hda_fixup alc269_fixups[] = {
@@ -8443,9 +8515,9 @@ static const struct hda_fixup alc269_fixups[] = {
                .chained = true,
                .chain_id = ALC269_FIXUP_HEADSET_MODE,
        },
-       [ALC256_FIXUP_TONGFANG_RESET_PERSISTENT_SETTINGS] = {
+       [ALC256_FIXUP_SET_COEF_DEFAULTS] = {
                .type = HDA_FIXUP_FUNC,
-               .v.func = alc256_fixup_tongfang_reset_persistent_settings,
+               .v.func = alc256_fixup_set_coef_defaults,
        },
        [ALC245_FIXUP_HP_GPIO_LED] = {
                .type = HDA_FIXUP_FUNC,
@@ -8460,6 +8532,16 @@ static const struct hda_fixup alc269_fixups[] = {
                .chained = true,
                .chain_id = ALC269_FIXUP_HEADSET_MODE_NO_HP_MIC,
        },
+       [ALC233_FIXUP_NO_AUDIO_JACK] = {
+               .type = HDA_FIXUP_FUNC,
+               .v.func = alc233_fixup_no_audio_jack,
+       },
+       [ALC256_FIXUP_MIC_NO_PRESENCE_AND_RESUME] = {
+               .type = HDA_FIXUP_FUNC,
+               .v.func = alc256_fixup_mic_no_presence_and_resume,
+               .chained = true,
+               .chain_id = ALC269_FIXUP_HEADSET_MODE_NO_HP_MIC
+       },
 };
 
 static const struct snd_pci_quirk alc269_fixup_tbl[] = {
@@ -8630,6 +8712,7 @@ static const struct snd_pci_quirk alc269_fixup_tbl[] = {
        SND_PCI_QUIRK(0x103c, 0x84da, "HP OMEN dc0019-ur", ALC295_FIXUP_HP_OMEN),
        SND_PCI_QUIRK(0x103c, 0x84e7, "HP Pavilion 15", ALC269_FIXUP_HP_MUTE_LED_MIC3),
        SND_PCI_QUIRK(0x103c, 0x8519, "HP Spectre x360 15-df0xxx", ALC285_FIXUP_HP_SPECTRE_X360),
+       SND_PCI_QUIRK(0x103c, 0x860f, "HP ZBook 15 G6", ALC285_FIXUP_HP_GPIO_AMP_INIT),
        SND_PCI_QUIRK(0x103c, 0x861f, "HP Elite Dragonfly G1", ALC285_FIXUP_HP_GPIO_AMP_INIT),
        SND_PCI_QUIRK(0x103c, 0x869d, "HP", ALC236_FIXUP_HP_MUTE_LED),
        SND_PCI_QUIRK(0x103c, 0x86c7, "HP Envy AiO 32", ALC274_FIXUP_HP_ENVY_GPIO),
@@ -8639,6 +8722,7 @@ static const struct snd_pci_quirk alc269_fixup_tbl[] = {
        SND_PCI_QUIRK(0x103c, 0x8728, "HP EliteBook 840 G7", ALC285_FIXUP_HP_GPIO_LED),
        SND_PCI_QUIRK(0x103c, 0x8729, "HP", ALC285_FIXUP_HP_GPIO_LED),
        SND_PCI_QUIRK(0x103c, 0x8730, "HP ProBook 445 G7", ALC236_FIXUP_HP_MUTE_LED_MICMUTE_VREF),
+       SND_PCI_QUIRK(0x103c, 0x8735, "HP ProBook 435 G7", ALC236_FIXUP_HP_MUTE_LED_MICMUTE_VREF),
        SND_PCI_QUIRK(0x103c, 0x8736, "HP", ALC285_FIXUP_HP_GPIO_AMP_INIT),
        SND_PCI_QUIRK(0x103c, 0x8760, "HP", ALC285_FIXUP_HP_MUTE_LED),
        SND_PCI_QUIRK(0x103c, 0x877a, "HP", ALC285_FIXUP_HP_MUTE_LED),
@@ -8674,6 +8758,8 @@ static const struct snd_pci_quirk alc269_fixup_tbl[] = {
        SND_PCI_QUIRK(0x103c, 0x8896, "HP EliteBook 855 G8 Notebook PC", ALC285_FIXUP_HP_MUTE_LED),
        SND_PCI_QUIRK(0x103c, 0x8898, "HP EliteBook 845 G8 Notebook PC", ALC285_FIXUP_HP_LIMIT_INT_MIC_BOOST),
        SND_PCI_QUIRK(0x103c, 0x88d0, "HP Pavilion 15-eh1xxx (mainboard 88D0)", ALC287_FIXUP_HP_GPIO_LED),
+       SND_PCI_QUIRK(0x103c, 0x89c3, "HP", ALC285_FIXUP_HP_GPIO_LED),
+       SND_PCI_QUIRK(0x103c, 0x89ca, "HP", ALC236_FIXUP_HP_MUTE_LED_MICMUTE_VREF),
        SND_PCI_QUIRK(0x1043, 0x103e, "ASUS X540SA", ALC256_FIXUP_ASUS_MIC),
        SND_PCI_QUIRK(0x1043, 0x103f, "ASUS TX300", ALC282_FIXUP_ASUS_TX300),
        SND_PCI_QUIRK(0x1043, 0x106d, "Asus K53BE", ALC269_FIXUP_LIMIT_INT_MIC_BOOST),
@@ -8798,7 +8884,7 @@ static const struct snd_pci_quirk alc269_fixup_tbl[] = {
        SND_PCI_QUIRK(0x1558, 0x8562, "Clevo NH[57][0-9]RZ[Q]", ALC269_FIXUP_DMIC),
        SND_PCI_QUIRK(0x1558, 0x8668, "Clevo NP50B[BE]", ALC293_FIXUP_SYSTEM76_MIC_NO_PRESENCE),
        SND_PCI_QUIRK(0x1558, 0x8680, "Clevo NJ50LU", ALC293_FIXUP_SYSTEM76_MIC_NO_PRESENCE),
-       SND_PCI_QUIRK(0x1558, 0x8686, "Clevo NH50[CZ]U", ALC293_FIXUP_SYSTEM76_MIC_NO_PRESENCE),
+       SND_PCI_QUIRK(0x1558, 0x8686, "Clevo NH50[CZ]U", ALC256_FIXUP_MIC_NO_PRESENCE_AND_RESUME),
        SND_PCI_QUIRK(0x1558, 0x8a20, "Clevo NH55DCQ-Y", ALC293_FIXUP_SYSTEM76_MIC_NO_PRESENCE),
        SND_PCI_QUIRK(0x1558, 0x8a51, "Clevo NH70RCQ-Y", ALC293_FIXUP_SYSTEM76_MIC_NO_PRESENCE),
        SND_PCI_QUIRK(0x1558, 0x8d50, "Clevo NH55RCQ-M", ALC293_FIXUP_SYSTEM76_MIC_NO_PRESENCE),
@@ -8870,6 +8956,7 @@ static const struct snd_pci_quirk alc269_fixup_tbl[] = {
        SND_PCI_QUIRK(0x17aa, 0x3813, "Legion 7i 15IMHG05", ALC287_FIXUP_LEGION_15IMHG05_SPEAKERS),
        SND_PCI_QUIRK(0x17aa, 0x3852, "Lenovo Yoga 7 14ITL5", ALC287_FIXUP_YOGA7_14ITL_SPEAKERS),
        SND_PCI_QUIRK(0x17aa, 0x3853, "Lenovo Yoga 7 15ITL5", ALC287_FIXUP_YOGA7_14ITL_SPEAKERS),
+       SND_PCI_QUIRK(0x17aa, 0x384a, "Lenovo Yoga 7 15ITL5", ALC287_FIXUP_YOGA7_14ITL_SPEAKERS),
        SND_PCI_QUIRK(0x17aa, 0x3819, "Lenovo 13s Gen2 ITL", ALC287_FIXUP_13S_GEN2_SPEAKERS),
        SND_PCI_QUIRK(0x17aa, 0x3902, "Lenovo E50-80", ALC269_FIXUP_DMIC_THINKPAD_ACPI),
        SND_PCI_QUIRK(0x17aa, 0x3977, "IdeaPad S210", ALC283_FIXUP_INT_MIC),
@@ -8894,6 +8981,7 @@ static const struct snd_pci_quirk alc269_fixup_tbl[] = {
        SND_PCI_QUIRK(0x17aa, 0x511e, "Thinkpad", ALC298_FIXUP_TPT470_DOCK),
        SND_PCI_QUIRK(0x17aa, 0x511f, "Thinkpad", ALC298_FIXUP_TPT470_DOCK),
        SND_PCI_QUIRK(0x17aa, 0x9e54, "LENOVO NB", ALC269_FIXUP_LENOVO_EAPD),
+       SND_PCI_QUIRK(0x1849, 0x1233, "ASRock NUC Box 1100", ALC233_FIXUP_NO_AUDIO_JACK),
        SND_PCI_QUIRK(0x19e5, 0x3204, "Huawei MACH-WX9", ALC256_FIXUP_HUAWEI_MACH_WX9_PINS),
        SND_PCI_QUIRK(0x1b35, 0x1235, "CZC B20", ALC269_FIXUP_CZC_B20),
        SND_PCI_QUIRK(0x1b35, 0x1236, "CZC TMI", ALC269_FIXUP_CZC_TMI),
@@ -8901,7 +8989,7 @@ static const struct snd_pci_quirk alc269_fixup_tbl[] = {
        SND_PCI_QUIRK(0x1b7d, 0xa831, "Ordissimo EVE2 ", ALC269VB_FIXUP_ORDISSIMO_EVE2), /* Also known as Malata PC-B1303 */
        SND_PCI_QUIRK(0x1c06, 0x2013, "Lemote A1802", ALC269_FIXUP_LEMOTE_A1802),
        SND_PCI_QUIRK(0x1c06, 0x2015, "Lemote A190X", ALC269_FIXUP_LEMOTE_A190X),
-       SND_PCI_QUIRK(0x1d05, 0x1132, "TongFang PHxTxX1", ALC256_FIXUP_TONGFANG_RESET_PERSISTENT_SETTINGS),
+       SND_PCI_QUIRK(0x1d05, 0x1132, "TongFang PHxTxX1", ALC256_FIXUP_SET_COEF_DEFAULTS),
        SND_PCI_QUIRK(0x1d72, 0x1602, "RedmiBook", ALC255_FIXUP_XIAOMI_HEADSET_MIC),
        SND_PCI_QUIRK(0x1d72, 0x1701, "XiaomiNotebook Pro", ALC298_FIXUP_DELL1_MIC_NO_PRESENCE),
        SND_PCI_QUIRK(0x1d72, 0x1901, "RedmiBook 14", ALC256_FIXUP_ASUS_HEADSET_MIC),
@@ -9091,6 +9179,7 @@ static const struct hda_model_fixup alc269_fixup_models[] = {
        {.id = ALC287_FIXUP_IDEAPAD_BASS_SPK_AMP, .name = "alc287-ideapad-bass-spk-amp"},
        {.id = ALC623_FIXUP_LENOVO_THINKSTATION_P340, .name = "alc623-lenovo-thinkstation-p340"},
        {.id = ALC255_FIXUP_ACER_HEADPHONE_AND_MIC, .name = "alc255-acer-headphone-and-mic"},
+       {.id = ALC285_FIXUP_HP_GPIO_AMP_INIT, .name = "alc285-hp-amp-init"},
        {}
 };
 #define ALC225_STANDARD_PINS \
@@ -10203,6 +10292,27 @@ static void alc671_fixup_hp_headset_mic2(struct hda_codec *codec,
        }
 }
 
+static void alc897_hp_automute_hook(struct hda_codec *codec,
+                                        struct hda_jack_callback *jack)
+{
+       struct alc_spec *spec = codec->spec;
+       int vref;
+
+       snd_hda_gen_hp_automute(codec, jack);
+       vref = spec->gen.hp_jack_present ? (PIN_HP | AC_PINCTL_VREF_100) : PIN_HP;
+       snd_hda_codec_write(codec, 0x1b, 0, AC_VERB_SET_PIN_WIDGET_CONTROL,
+                           vref);
+}
+
+static void alc897_fixup_lenovo_headset_mic(struct hda_codec *codec,
+                                    const struct hda_fixup *fix, int action)
+{
+       struct alc_spec *spec = codec->spec;
+       if (action == HDA_FIXUP_ACT_PRE_PROBE) {
+               spec->gen.hp_automute_hook = alc897_hp_automute_hook;
+       }
+}
+
 static const struct coef_fw alc668_coefs[] = {
        WRITE_COEF(0x01, 0xbebe), WRITE_COEF(0x02, 0xaaaa), WRITE_COEF(0x03,    0x0),
        WRITE_COEF(0x04, 0x0180), WRITE_COEF(0x06,    0x0), WRITE_COEF(0x07, 0x0f80),
@@ -10283,6 +10393,8 @@ enum {
        ALC668_FIXUP_ASUS_NO_HEADSET_MIC,
        ALC668_FIXUP_HEADSET_MIC,
        ALC668_FIXUP_MIC_DET_COEF,
+       ALC897_FIXUP_LENOVO_HEADSET_MIC,
+       ALC897_FIXUP_HEADSET_MIC_PIN,
 };
 
 static const struct hda_fixup alc662_fixups[] = {
@@ -10689,6 +10801,19 @@ static const struct hda_fixup alc662_fixups[] = {
                        {}
                },
        },
+       [ALC897_FIXUP_LENOVO_HEADSET_MIC] = {
+               .type = HDA_FIXUP_FUNC,
+               .v.func = alc897_fixup_lenovo_headset_mic,
+       },
+       [ALC897_FIXUP_HEADSET_MIC_PIN] = {
+               .type = HDA_FIXUP_PINS,
+               .v.pins = (const struct hda_pintbl[]) {
+                       { 0x1a, 0x03a11050 },
+                       { }
+               },
+               .chained = true,
+               .chain_id = ALC897_FIXUP_LENOVO_HEADSET_MIC
+       },
 };
 
 static const struct snd_pci_quirk alc662_fixup_tbl[] = {
@@ -10733,6 +10858,10 @@ static const struct snd_pci_quirk alc662_fixup_tbl[] = {
        SND_PCI_QUIRK(0x144d, 0xc051, "Samsung R720", ALC662_FIXUP_IDEAPAD),
        SND_PCI_QUIRK(0x14cd, 0x5003, "USI", ALC662_FIXUP_USI_HEADSET_MODE),
        SND_PCI_QUIRK(0x17aa, 0x1036, "Lenovo P520", ALC662_FIXUP_LENOVO_MULTI_CODECS),
+       SND_PCI_QUIRK(0x17aa, 0x32ca, "Lenovo ThinkCentre M80", ALC897_FIXUP_HEADSET_MIC_PIN),
+       SND_PCI_QUIRK(0x17aa, 0x32cb, "Lenovo ThinkCentre M70", ALC897_FIXUP_HEADSET_MIC_PIN),
+       SND_PCI_QUIRK(0x17aa, 0x32cf, "Lenovo ThinkCentre M950", ALC897_FIXUP_HEADSET_MIC_PIN),
+       SND_PCI_QUIRK(0x17aa, 0x32f7, "Lenovo ThinkCentre M90", ALC897_FIXUP_HEADSET_MIC_PIN),
        SND_PCI_QUIRK(0x17aa, 0x38af, "Lenovo Ideapad Y550P", ALC662_FIXUP_IDEAPAD),
        SND_PCI_QUIRK(0x17aa, 0x3a0d, "Lenovo Ideapad Y550", ALC662_FIXUP_IDEAPAD),
        SND_PCI_QUIRK(0x1849, 0x5892, "ASRock B150M", ALC892_FIXUP_ASROCK_MOBO),
index fb8895a..a047ed0 100644 (file)
@@ -23,8 +23,6 @@
 #define MSG_DESCRIPTOR_SIZE         0x24
 #define MSG_HEADER_SIZE             (MSG_DESCRIPTOR_SIZE + 4)
 
-#define MSG_DEFAULT_SIZE            512
-
 #define MSG_TYPE_MASK               0x00000003    /* mask for following types */
 #define MSG_TYPE_NOTIFY             0             /* embedded -> driver (only notification, do not get_msg() !) */
 #define MSG_TYPE_COMMAND            1             /* driver <-> embedded (a command has no answer) */
@@ -444,6 +442,9 @@ irqreturn_t snd_mixart_threaded_irq(int irq, void *dev_id)
                                struct mixart_timer_notify *notify;
                                notify = (struct mixart_timer_notify *)mixart_msg_data;
 
+                               BUILD_BUG_ON(sizeof(notify) > sizeof(mixart_msg_data));
+                               if (snd_BUG_ON(notify->stream_count > ARRAY_SIZE(notify->streams)))
+                                       break;
                                for(i=0; i<notify->stream_count; i++) {
 
                                        u32 buffer_id = notify->streams[i].buffer_id;
index fbf4731..2f0e29e 100644 (file)
@@ -49,6 +49,7 @@ enum mixart_message_id {
        MSG_CLOCK_SET_PROPERTIES             = 0x200002,
 };
 
+#define MSG_DEFAULT_SIZE            512
 
 struct mixart_msg
 {
@@ -251,10 +252,17 @@ struct mixart_sample_pos
        u32   sample_pos_low_part;
 } __attribute__((packed));
 
+/*
+ * This structure is limited by the size of MSG_DEFAULT_SIZE. Instead of
+ * having MIXART_MAX_STREAM_PER_CARD * MIXART_MAX_CARDS many streams,
+ * this is capped to have a total size below MSG_DEFAULT_SIZE.
+ */
+#define MIXART_MAX_TIMER_NOTIFY_STREAMS                                \
+       ((MSG_DEFAULT_SIZE - sizeof(u32)) / sizeof(struct mixart_sample_pos))
 struct mixart_timer_notify
 {
        u32                  stream_count;
-       struct mixart_sample_pos  streams[MIXART_MAX_STREAM_PER_CARD * MIXART_MAX_CARDS];
+       struct mixart_sample_pos  streams[MIXART_MAX_TIMER_NOTIFY_STREAMS];
 } __attribute__((packed));
 
 
index 0f4bce1..bf28978 100644 (file)
@@ -99,7 +99,7 @@ static int snd_pmac_beep_event(struct input_dev *dev, unsigned int type,
                return -1;
 
        switch (code) {
-       case SND_BELL: if (hz) hz = 1000;
+       case SND_BELL: if (hz) hz = 1000; break;
        case SND_TONE: break;
        default: return -1;
        }
index f16900e..80bf560 100644 (file)
@@ -14,7 +14,7 @@
 #ifndef _ASOC_STA_350_H
 #define _ASOC_STA_350_H
 
-/* STA50 register addresses */
+/* STA350 register addresses */
 
 #define STA350_REGISTER_COUNT  0x4D
 #define STA350_COEF_COUNT 62
index 1f2879b..c865698 100644 (file)
@@ -6,8 +6,8 @@
  * Copyright (C) 2008 Secret Lab Technologies Ltd.
  */
 
-#ifndef _TLV320AIC16_H_
-#define _TLV320AIC16_H_
+#ifndef _TLV320AIC26_H_
+#define _TLV320AIC26_H_
 
 /* AIC26 Registers */
 #define AIC26_PAGE_ADDR(page, offset)  ((page << 11) | offset << 5)
@@ -88,4 +88,4 @@ enum aic26_wlen {
        AIC26_WLEN_32   = 3 << 10,
 };
 
-#endif /* _TLV320AIC16_H_ */
+#endif /* _TLV320AIC26_H_ */
index 6422607..2439a57 100644 (file)
 #include "skl.h"
 #include "skl-i2s.h"
 
-static struct nhlt_specific_cfg *skl_get_specific_cfg(
-               struct device *dev, struct nhlt_fmt *fmt,
-               u8 no_ch, u32 rate, u16 bps, u8 linktype)
-{
-       struct nhlt_specific_cfg *sp_config;
-       struct wav_fmt *wfmt;
-       struct nhlt_fmt_cfg *fmt_config = fmt->fmt_config;
-       int i;
-
-       dev_dbg(dev, "Format count =%d\n", fmt->fmt_count);
-
-       for (i = 0; i < fmt->fmt_count; i++) {
-               wfmt = &fmt_config->fmt_ext.fmt;
-               dev_dbg(dev, "ch=%d fmt=%d s_rate=%d\n", wfmt->channels,
-                        wfmt->bits_per_sample, wfmt->samples_per_sec);
-               if (wfmt->channels == no_ch && wfmt->bits_per_sample == bps) {
-                       /*
-                        * if link type is dmic ignore rate check as the blob is
-                        * generic for all rates
-                        */
-                       sp_config = &fmt_config->config;
-                       if (linktype == NHLT_LINK_DMIC)
-                               return sp_config;
-
-                       if (wfmt->samples_per_sec == rate)
-                               return sp_config;
-               }
-
-               fmt_config = (struct nhlt_fmt_cfg *)(fmt_config->config.caps +
-                                               fmt_config->config.size);
-       }
-
-       return NULL;
-}
-
-static void dump_config(struct device *dev, u32 instance_id, u8 linktype,
-               u8 s_fmt, u8 num_channels, u32 s_rate, u8 dirn, u16 bps)
-{
-       dev_dbg(dev, "Input configuration\n");
-       dev_dbg(dev, "ch=%d fmt=%d s_rate=%d\n", num_channels, s_fmt, s_rate);
-       dev_dbg(dev, "vbus_id=%d link_type=%d\n", instance_id, linktype);
-       dev_dbg(dev, "bits_per_sample=%d\n", bps);
-}
-
-static bool skl_check_ep_match(struct device *dev, struct nhlt_endpoint *epnt,
-               u32 instance_id, u8 link_type, u8 dirn, u8 dev_type)
-{
-       dev_dbg(dev, "vbus_id=%d link_type=%d dir=%d dev_type = %d\n",
-                       epnt->virtual_bus_id, epnt->linktype,
-                       epnt->direction, epnt->device_type);
-
-       if ((epnt->virtual_bus_id == instance_id) &&
-                       (epnt->linktype == link_type) &&
-                       (epnt->direction == dirn)) {
-               /* do not check dev_type for DMIC link type */
-               if (epnt->linktype == NHLT_LINK_DMIC)
-                       return true;
-
-               if (epnt->device_type == dev_type)
-                       return true;
-       }
-
-       return false;
-}
-
-struct nhlt_specific_cfg
-*skl_get_ep_blob(struct skl_dev *skl, u32 instance, u8 link_type,
-                       u8 s_fmt, u8 num_ch, u32 s_rate,
-                       u8 dirn, u8 dev_type)
-{
-       struct nhlt_fmt *fmt;
-       struct nhlt_endpoint *epnt;
-       struct hdac_bus *bus = skl_to_bus(skl);
-       struct device *dev = bus->dev;
-       struct nhlt_specific_cfg *sp_config;
-       struct nhlt_acpi_table *nhlt = skl->nhlt;
-       u16 bps = (s_fmt == 16) ? 16 : 32;
-       u8 j;
-
-       dump_config(dev, instance, link_type, s_fmt, num_ch, s_rate, dirn, bps);
-
-       epnt = (struct nhlt_endpoint *)nhlt->desc;
-
-       dev_dbg(dev, "endpoint count =%d\n", nhlt->endpoint_count);
-
-       for (j = 0; j < nhlt->endpoint_count; j++) {
-               if (skl_check_ep_match(dev, epnt, instance, link_type,
-                                               dirn, dev_type)) {
-                       fmt = (struct nhlt_fmt *)(epnt->config.caps +
-                                                epnt->config.size);
-                       sp_config = skl_get_specific_cfg(dev, fmt, num_ch,
-                                                       s_rate, bps, link_type);
-                       if (sp_config)
-                               return sp_config;
-               }
-
-               epnt = (struct nhlt_endpoint *)((u8 *)epnt + epnt->length);
-       }
-
-       return NULL;
-}
-
 static void skl_nhlt_trim_space(char *trim)
 {
        char *s = trim;
index 8378c18..55f310e 100644 (file)
@@ -317,6 +317,7 @@ static int skl_pcm_hw_params(struct snd_pcm_substream *substream,
        dev_dbg(dai->dev, "dma_id=%d\n", dma_id);
 
        p_params.s_fmt = snd_pcm_format_width(params_format(params));
+       p_params.s_cont = snd_pcm_format_physical_width(params_format(params));
        p_params.ch = params_channels(params);
        p_params.s_freq = params_rate(params);
        p_params.host_dma_id = dma_id;
@@ -405,6 +406,7 @@ static int skl_be_hw_params(struct snd_pcm_substream *substream,
        struct skl_pipe_params p_params = {0};
 
        p_params.s_fmt = snd_pcm_format_width(params_format(params));
+       p_params.s_cont = snd_pcm_format_physical_width(params_format(params));
        p_params.ch = params_channels(params);
        p_params.s_freq = params_rate(params);
        p_params.stream = substream->stream;
@@ -566,6 +568,7 @@ static int skl_link_hw_params(struct snd_pcm_substream *substream,
        snd_soc_dai_set_stream(codec_dai, hdac_stream(link_dev), substream->stream);
 
        p_params.s_fmt = snd_pcm_format_width(params_format(params));
+       p_params.s_cont = snd_pcm_format_physical_width(params_format(params));
        p_params.ch = params_channels(params);
        p_params.s_freq = params_rate(params);
        p_params.stream = substream->stream;
@@ -1248,7 +1251,6 @@ static int skl_platform_soc_get_time_info(
                snd_pcm_gettime(substream->runtime, system_ts);
 
                nsec = timecounter_read(&hstr->tc);
-               nsec = div_u64(nsec, 3); /* can be optimized */
                if (audio_tstamp_config->report_delay)
                        nsec = skl_adjust_codec_delay(substream, nsec);
 
index 89e4231..9bdf020 100644 (file)
@@ -285,7 +285,7 @@ static int skl_tplg_update_be_blob(struct snd_soc_dapm_widget *w,
 {
        struct skl_module_cfg *m_cfg = w->priv;
        int link_type, dir;
-       u32 ch, s_freq, s_fmt;
+       u32 ch, s_freq, s_fmt, s_cont;
        struct nhlt_specific_cfg *cfg;
        u8 dev_type = skl_tplg_be_dev_type(m_cfg->dev_type);
        int fmt_idx = m_cfg->fmt_idx;
@@ -301,7 +301,8 @@ static int skl_tplg_update_be_blob(struct snd_soc_dapm_widget *w,
                link_type = NHLT_LINK_DMIC;
                dir = SNDRV_PCM_STREAM_CAPTURE;
                s_freq = m_iface->inputs[0].fmt.s_freq;
-               s_fmt = m_iface->inputs[0].fmt.bit_depth;
+               s_fmt = m_iface->inputs[0].fmt.valid_bit_depth;
+               s_cont = m_iface->inputs[0].fmt.bit_depth;
                ch = m_iface->inputs[0].fmt.channels;
                break;
 
@@ -310,12 +311,14 @@ static int skl_tplg_update_be_blob(struct snd_soc_dapm_widget *w,
                if (m_cfg->hw_conn_type == SKL_CONN_SOURCE) {
                        dir = SNDRV_PCM_STREAM_PLAYBACK;
                        s_freq = m_iface->outputs[0].fmt.s_freq;
-                       s_fmt = m_iface->outputs[0].fmt.bit_depth;
+                       s_fmt = m_iface->outputs[0].fmt.valid_bit_depth;
+                       s_cont = m_iface->outputs[0].fmt.bit_depth;
                        ch = m_iface->outputs[0].fmt.channels;
                } else {
                        dir = SNDRV_PCM_STREAM_CAPTURE;
                        s_freq = m_iface->inputs[0].fmt.s_freq;
-                       s_fmt = m_iface->inputs[0].fmt.bit_depth;
+                       s_fmt = m_iface->inputs[0].fmt.valid_bit_depth;
+                       s_cont = m_iface->inputs[0].fmt.bit_depth;
                        ch = m_iface->inputs[0].fmt.channels;
                }
                break;
@@ -325,16 +328,17 @@ static int skl_tplg_update_be_blob(struct snd_soc_dapm_widget *w,
        }
 
        /* update the blob based on virtual bus_id and default params */
-       cfg = skl_get_ep_blob(skl, m_cfg->vbus_id, link_type,
-                                       s_fmt, ch, s_freq, dir, dev_type);
+       cfg = intel_nhlt_get_endpoint_blob(skl->dev, skl->nhlt, m_cfg->vbus_id,
+                                          link_type, s_fmt, s_cont, ch,
+                                          s_freq, dir, dev_type);
        if (cfg) {
                m_cfg->formats_config[SKL_PARAM_INIT].caps_size = cfg->size;
                m_cfg->formats_config[SKL_PARAM_INIT].caps = (u32 *)&cfg->caps;
        } else {
                dev_err(skl->dev, "Blob NULL for id %x type %d dirn %d\n",
                                        m_cfg->vbus_id, link_type, dir);
-               dev_err(skl->dev, "PCM: ch %d, freq %d, fmt %d\n",
-                                       ch, s_freq, s_fmt);
+               dev_err(skl->dev, "PCM: ch %d, freq %d, fmt %d/%d\n",
+                                       ch, s_freq, s_fmt, s_cont);
                return -EIO;
        }
 
@@ -1849,10 +1853,11 @@ static int skl_tplg_be_fill_pipe_params(struct snd_soc_dai *dai,
                pipe_fmt = &pipe->configs[pipe->pipe_config_idx].in_fmt;
 
        /* update the blob based on virtual bus_id*/
-       cfg = skl_get_ep_blob(skl, mconfig->vbus_id, link_type,
-                                       pipe_fmt->bps, pipe_fmt->channels,
-                                       pipe_fmt->freq, pipe->direction,
-                                       dev_type);
+       cfg = intel_nhlt_get_endpoint_blob(dai->dev, skl->nhlt,
+                                       mconfig->vbus_id, link_type,
+                                       pipe_fmt->bps, params->s_cont,
+                                       pipe_fmt->channels, pipe_fmt->freq,
+                                       pipe->direction, dev_type);
        if (cfg) {
                mconfig->formats_config[SKL_PARAM_INIT].caps_size = cfg->size;
                mconfig->formats_config[SKL_PARAM_INIT].caps = (u32 *)&cfg->caps;
index f0695b2..2296363 100644 (file)
@@ -284,6 +284,7 @@ struct skl_pipe_params {
        u32 ch;
        u32 s_freq;
        u32 s_fmt;
+       u32 s_cont;
        u8 linktype;
        snd_pcm_format_t format;
        int link_index;
index 5b1a15e..148ddf4 100644 (file)
@@ -439,7 +439,7 @@ static int skl_free(struct hdac_bus *bus)
 
        skl->init_done = 0; /* to be sure */
 
-       snd_hdac_ext_stop_streams(bus);
+       snd_hdac_stop_streams_and_chip(bus);
 
        if (bus->irq >= 0)
                free_irq(bus->irq, (void *)bus);
@@ -1096,7 +1096,7 @@ static void skl_shutdown(struct pci_dev *pci)
        if (!skl->init_done)
                return;
 
-       snd_hdac_ext_stop_streams(bus);
+       snd_hdac_stop_streams_and_chip(bus);
        list_for_each_entry(s, &bus->stream_list, list) {
                stream = stream_to_hdac_ext_stream(s);
                snd_hdac_ext_stream_decouple(bus, stream, false);
index 33ed274..f55f8b3 100644 (file)
@@ -165,10 +165,6 @@ struct skl_dsp_ops {
 int skl_platform_unregister(struct device *dev);
 int skl_platform_register(struct device *dev);
 
-struct nhlt_specific_cfg *skl_get_ep_blob(struct skl_dev *skl, u32 instance,
-                                       u8 link_type, u8 s_fmt, u8 num_ch,
-                                       u32 s_rate, u8 dirn, u8 dev_type);
-
 int skl_nhlt_update_topology_bin(struct skl_dev *skl);
 int skl_init_dsp(struct skl_dev *skl);
 int skl_free_dsp(struct skl_dev *skl);
index 6b84f66..3881e1c 100644 (file)
@@ -688,7 +688,7 @@ static void dbri_cmdsend(struct snd_dbri *dbri, s32 *cmd, int len)
 {
        u32 dvma_addr = (u32)dbri->dma_dvma;
        s32 tmp, addr;
-       static int wait_id = 0;
+       static int wait_id;
 
        wait_id++;
        wait_id &= 0xffff;      /* restrict it to a 16 bit counter. */
@@ -1926,7 +1926,7 @@ static void dbri_process_interrupt_buffer(struct snd_dbri *dbri)
 static irqreturn_t snd_dbri_interrupt(int irq, void *dev_id)
 {
        struct snd_dbri *dbri = dev_id;
-       static int errcnt = 0;
+       static int errcnt;
        int x;
 
        if (dbri == NULL)
@@ -2591,7 +2591,7 @@ static int dbri_probe(struct platform_device *op)
        struct snd_dbri *dbri;
        struct resource *rp;
        struct snd_card *card;
-       static int dev = 0;
+       static int dev;
        int irq;
        int err;
 
index 1764b93..3769622 100644 (file)
@@ -987,8 +987,6 @@ void snd_usb_unlock_shutdown(struct snd_usb_audio *chip)
                wake_up(&chip->shutdown_wait);
 }
 
-#ifdef CONFIG_PM
-
 int snd_usb_autoresume(struct snd_usb_audio *chip)
 {
        int i, err;
@@ -1100,11 +1098,6 @@ err_out:
        atomic_dec(&chip->active); /* allow autopm after this point */
        return err;
 }
-#else
-#define usb_audio_suspend      NULL
-#define usb_audio_resume       NULL
-#define usb_audio_resume       NULL
-#endif         /* CONFIG_PM */
 
 static const struct usb_device_id usb_audio_ids [] = {
 #include "quirks-table.h"
index f5e676a..405dc0b 100644 (file)
@@ -375,7 +375,7 @@ static int parse_uac2_sample_rate_range(struct snd_usb_audio *chip,
                for (rate = min; rate <= max; rate += res) {
 
                        /* Filter out invalid rates on Presonus Studio 1810c */
-                       if (chip->usb_id == USB_ID(0x0194f, 0x010c) &&
+                       if (chip->usb_id == USB_ID(0x194f, 0x010c) &&
                            !s1810c_valid_sample_rate(fp, rate))
                                goto skip_rate;
 
index 6e7bac8..e8f3f8d 100644 (file)
@@ -145,6 +145,7 @@ static inline void check_mapped_dB(const struct usbmix_name_map *p,
        if (p && p->dB) {
                cval->dBmin = p->dB->min;
                cval->dBmax = p->dB->max;
+               cval->min_mute = p->dB->min_mute;
                cval->initialized = 1;
        }
 }
@@ -3628,7 +3629,6 @@ void snd_usb_mixer_disconnect(struct usb_mixer_interface *mixer)
        mixer->disconnected = true;
 }
 
-#ifdef CONFIG_PM
 /* stop any bus activity of a mixer */
 static void snd_usb_mixer_inactivate(struct usb_mixer_interface *mixer)
 {
@@ -3710,7 +3710,6 @@ int snd_usb_mixer_resume(struct usb_mixer_interface *mixer)
 
        return snd_usb_mixer_activate(mixer);
 }
-#endif
 
 void snd_usb_mixer_elem_init_std(struct usb_mixer_elem_list *list,
                                 struct usb_mixer_interface *mixer,
@@ -3719,7 +3718,5 @@ void snd_usb_mixer_elem_init_std(struct usb_mixer_elem_list *list,
        list->mixer = mixer;
        list->id = unitid;
        list->dump = snd_usb_mixer_dump_cval;
-#ifdef CONFIG_PM
        list->resume = restore_mixer_value;
-#endif
 }
index 98ea24d..d43895c 100644 (file)
@@ -118,10 +118,8 @@ void snd_usb_mixer_elem_init_std(struct usb_mixer_elem_list *list,
 int snd_usb_mixer_vol_tlv(struct snd_kcontrol *kcontrol, int op_flag,
                          unsigned int size, unsigned int __user *_tlv);
 
-#ifdef CONFIG_PM
 int snd_usb_mixer_suspend(struct usb_mixer_interface *mixer);
 int snd_usb_mixer_resume(struct usb_mixer_interface *mixer);
-#endif
 
 int snd_usb_set_cur_mix_value(struct usb_mixer_elem_info *cval, int channel,
                              int index, int value);
index 55eea90..5d391f6 100644 (file)
@@ -6,8 +6,9 @@
  */
 
 struct usbmix_dB_map {
-       u32 min;
-       u32 max;
+       int min;
+       int max;
+       bool min_mute;
 };
 
 struct usbmix_name_map {
@@ -336,6 +337,13 @@ static const struct usbmix_name_map bose_companion5_map[] = {
        { 0 }   /* terminator */
 };
 
+/* Bose Revolve+ SoundLink, correction of dB maps */
+static const struct usbmix_dB_map bose_soundlink_dB = {-8283, -0, true};
+static const struct usbmix_name_map bose_soundlink_map[] = {
+       { 2, NULL, .dB = &bose_soundlink_dB },
+       { 0 }   /* terminator */
+};
+
 /* Sennheiser Communications Headset [PC 8], the dB value is reported as -6 negative maximum  */
 static const struct usbmix_dB_map sennheiser_pc8_dB = {-9500, 0};
 static const struct usbmix_name_map sennheiser_pc8_map[] = {
@@ -521,6 +529,11 @@ static const struct usbmix_ctl_map usbmix_ctl_maps[] = {
                .id = USB_ID(0x05a7, 0x1020),
                .map = bose_companion5_map,
        },
+       {
+               /* Bose Revolve+ SoundLink */
+               .id = USB_ID(0x05a7, 0x40fa),
+               .map = bose_soundlink_map,
+       },
        {
                /* Corsair Virtuoso SE (wired mode) */
                .id = USB_ID(0x1b1c, 0x0a3d),
index d489c1d..e447ddd 100644 (file)
@@ -3016,11 +3016,11 @@ static const struct snd_djm_ctl snd_djm_ctls_750mk2[] = {
 
 
 static const struct snd_djm_device snd_djm_devices[] = {
-       SND_DJM_DEVICE(250mk2),
-       SND_DJM_DEVICE(750),
-       SND_DJM_DEVICE(750mk2),
-       SND_DJM_DEVICE(850),
-       SND_DJM_DEVICE(900nxs2)
+       [SND_DJM_250MK2_IDX] = SND_DJM_DEVICE(250mk2),
+       [SND_DJM_750_IDX] = SND_DJM_DEVICE(750),
+       [SND_DJM_850_IDX] = SND_DJM_DEVICE(850),
+       [SND_DJM_900NXS2_IDX] = SND_DJM_DEVICE(900nxs2),
+       [SND_DJM_750MK2_IDX] = SND_DJM_DEVICE(750mk2),
 };
 
 
@@ -3254,7 +3254,7 @@ int snd_usb_mixer_apply_create_quirk(struct usb_mixer_interface *mixer)
                err = snd_rme_controls_create(mixer);
                break;
 
-       case USB_ID(0x0194f, 0x010c): /* Presonus Studio 1810c */
+       case USB_ID(0x194f, 0x010c): /* Presonus Studio 1810c */
                err = snd_sc1810_init_mixer(mixer);
                break;
        case USB_ID(0x2a39, 0x3fb0): /* RME Babyface Pro FS */
@@ -3280,7 +3280,6 @@ int snd_usb_mixer_apply_create_quirk(struct usb_mixer_interface *mixer)
        return err;
 }
 
-#ifdef CONFIG_PM
 void snd_usb_mixer_resume_quirk(struct usb_mixer_interface *mixer)
 {
        switch (mixer->chip->usb_id) {
@@ -3289,7 +3288,6 @@ void snd_usb_mixer_resume_quirk(struct usb_mixer_interface *mixer)
                break;
        }
 }
-#endif
 
 void snd_usb_mixer_rc_memory_change(struct usb_mixer_interface *mixer,
                                    int unitid)
index 52be26d..4ba01ba 100644 (file)
@@ -14,9 +14,7 @@ void snd_usb_mixer_fu_apply_quirk(struct usb_mixer_interface *mixer,
                                  struct usb_mixer_elem_info *cval, int unitid,
                                  struct snd_kcontrol *kctl);
 
-#ifdef CONFIG_PM
 void snd_usb_mixer_resume_quirk(struct usb_mixer_interface *mixer);
-#endif
 
 #endif /* SND_USB_MIXER_QUIRKS_H */
 
index 95ec8ee..cec6e91 100644 (file)
@@ -581,6 +581,12 @@ static int snd_usb_hw_free(struct snd_pcm_substream *substream)
        return 0;
 }
 
+/* free-wheeling mode? (e.g. dmix) */
+static int in_free_wheeling_mode(struct snd_pcm_runtime *runtime)
+{
+       return runtime->stop_threshold > runtime->buffer_size;
+}
+
 /* check whether early start is needed for playback stream */
 static int lowlatency_playback_available(struct snd_pcm_runtime *runtime,
                                         struct snd_usb_substream *subs)
@@ -592,8 +598,7 @@ static int lowlatency_playback_available(struct snd_pcm_runtime *runtime,
        /* disabled via module option? */
        if (!chip->lowlatency)
                return false;
-       /* free-wheeling mode? (e.g. dmix) */
-       if (runtime->stop_threshold > runtime->buffer_size)
+       if (in_free_wheeling_mode(runtime))
                return false;
        /* implicit feedback mode has own operation mode */
        if (snd_usb_endpoint_implicit_feedback_sink(subs->data_endpoint))
@@ -635,7 +640,8 @@ static int snd_usb_pcm_prepare(struct snd_pcm_substream *substream)
        runtime->delay = 0;
 
        subs->lowlatency_playback = lowlatency_playback_available(runtime, subs);
-       if (!subs->lowlatency_playback)
+       if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK &&
+           !subs->lowlatency_playback)
                ret = start_endpoints(subs);
 
  unlock:
@@ -1552,6 +1558,8 @@ static int snd_usb_substream_playback_trigger(struct snd_pcm_substream *substrea
                                              subs);
                if (subs->lowlatency_playback &&
                    cmd == SNDRV_PCM_TRIGGER_START) {
+                       if (in_free_wheeling_mode(substream->runtime))
+                               subs->lowlatency_playback = false;
                        err = start_endpoints(subs);
                        if (err < 0) {
                                snd_usb_endpoint_set_callback(subs->data_endpoint,
index 6004231..396e3e5 100644 (file)
@@ -21,17 +21,7 @@ struct snd_usb_power_domain *
 snd_usb_find_power_domain(struct usb_host_interface *ctrl_iface,
                          unsigned char id);
 
-#ifdef CONFIG_PM
 int snd_usb_autoresume(struct snd_usb_audio *chip);
 void snd_usb_autosuspend(struct snd_usb_audio *chip);
-#else
-static inline int snd_usb_autoresume(struct snd_usb_audio *chip)
-{
-       return 0;
-}
-static inline void snd_usb_autosuspend(struct snd_usb_audio *chip)
-{
-}
-#endif
 
 #endif /* __USBAUDIO_POWER_H */
index 64e1c20..ab9f3da 100644 (file)
@@ -1290,7 +1290,7 @@ int snd_usb_apply_interface_quirk(struct snd_usb_audio *chip,
        if (chip->usb_id == USB_ID(0x0763, 0x2012))
                return fasttrackpro_skip_setting_quirk(chip, iface, altno);
        /* presonus studio 1810c: skip altsets incompatible with device_setup */
-       if (chip->usb_id == USB_ID(0x0194f, 0x010c))
+       if (chip->usb_id == USB_ID(0x194f, 0x010c))
                return s1810c_skip_setting_quirk(chip, iface, altno);
 
 
index 099bee6..52f4e66 100644 (file)
@@ -1,6 +1,6 @@
 // SPDX-License-Identifier: GPL-2.0-or-later
 /*
- * usbusy2y.c - ALSA USB US-428 Driver
+ * usbusx2y.c - ALSA USB US-428 Driver
  *
 2005-04-14 Karsten Wiese
        Version 0.8.7.2:
index c852eb4..d08fe4c 100644 (file)
@@ -1,5 +1,6 @@
 # SPDX-License-Identifier: GPL-2.0
-TARGETS = arm64
+TARGETS += alsa
+TARGETS += arm64
 TARGETS += bpf
 TARGETS += breakpoints
 TARGETS += capabilities
diff --git a/tools/testing/selftests/alsa/.gitignore b/tools/testing/selftests/alsa/.gitignore
new file mode 100644 (file)
index 0000000..3bb7c41
--- /dev/null
@@ -0,0 +1 @@
+mixer-test
diff --git a/tools/testing/selftests/alsa/Makefile b/tools/testing/selftests/alsa/Makefile
new file mode 100644 (file)
index 0000000..f64d909
--- /dev/null
@@ -0,0 +1,9 @@
+# SPDX-License-Identifier: GPL-2.0
+#
+
+CFLAGS += $(shell pkg-config --cflags alsa)
+LDLIBS += $(shell pkg-config --libs alsa)
+
+TEST_GEN_PROGS := mixer-test
+
+include ../lib.mk
diff --git a/tools/testing/selftests/alsa/mixer-test.c b/tools/testing/selftests/alsa/mixer-test.c
new file mode 100644 (file)
index 0000000..17f158d
--- /dev/null
@@ -0,0 +1,705 @@
+// SPDX-License-Identifier: GPL-2.0
+//
+// kselftest for the ALSA mixer API
+//
+// Original author: Mark Brown <broonie@kernel.org>
+// Copyright (c) 2021 Arm Limited
+
+// This test will iterate over all cards detected in the system, exercising
+// every mixer control it can find.  This may conflict with other system
+// software if there is audio activity so is best run on a system with a
+// minimal active userspace.
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdbool.h>
+#include <string.h>
+#include <getopt.h>
+#include <stdarg.h>
+#include <ctype.h>
+#include <math.h>
+#include <errno.h>
+#include <assert.h>
+#include <alsa/asoundlib.h>
+#include <poll.h>
+#include <stdint.h>
+
+#include "../kselftest.h"
+
+#define TESTS_PER_CONTROL 3
+
+struct card_data {
+       snd_ctl_t *handle;
+       int card;
+       int num_ctls;
+       snd_ctl_elem_list_t *ctls;
+       struct card_data *next;
+};
+
+struct ctl_data {
+       const char *name;
+       snd_ctl_elem_id_t *id;
+       snd_ctl_elem_info_t *info;
+       snd_ctl_elem_value_t *def_val;
+       int elem;
+       struct card_data *card;
+       struct ctl_data *next;
+};
+
+static const char *alsa_config =
+"ctl.hw {\n"
+"      @args [ CARD ]\n"
+"      @args.CARD.type string\n"
+"      type hw\n"
+"      card $CARD\n"
+"}\n"
+;
+
+int num_cards = 0;
+int num_controls = 0;
+struct card_data *card_list = NULL;
+struct ctl_data *ctl_list = NULL;
+
+#ifdef SND_LIB_VER
+#if SND_LIB_VERSION >= SND_LIB_VER(1, 2, 6)
+#define LIB_HAS_LOAD_STRING
+#endif
+#endif
+
+#ifndef LIB_HAS_LOAD_STRING
+int snd_config_load_string(snd_config_t **config, const char *s, size_t size)
+{
+       snd_input_t *input;
+       snd_config_t *dst;
+       int err;
+
+       assert(config && s);
+       if (size == 0)
+               size = strlen(s);
+       err = snd_input_buffer_open(&input, s, size);
+       if (err < 0)
+               return err;
+       err = snd_config_top(&dst);
+       if (err < 0) {
+               snd_input_close(input);
+               return err;
+       }
+       err = snd_config_load(dst, input);
+       snd_input_close(input);
+       if (err < 0) {
+               snd_config_delete(dst);
+               return err;
+       }
+       *config = dst;
+       return 0;
+}
+#endif
+
+void find_controls(void)
+{
+       char name[32];
+       int card, ctl, err;
+       struct card_data *card_data;
+       struct ctl_data *ctl_data;
+       snd_config_t *config;
+
+       card = -1;
+       if (snd_card_next(&card) < 0 || card < 0)
+               return;
+
+       err = snd_config_load_string(&config, alsa_config, strlen(alsa_config));
+       if (err < 0) {
+               ksft_print_msg("Unable to parse custom alsa-lib configuration: %s\n",
+                              snd_strerror(err));
+               ksft_exit_fail();
+       }
+
+       while (card >= 0) {
+               sprintf(name, "hw:%d", card);
+
+               card_data = malloc(sizeof(*card_data));
+               if (!card_data)
+                       ksft_exit_fail_msg("Out of memory\n");
+
+               err = snd_ctl_open_lconf(&card_data->handle, name, 0, config);
+               if (err < 0) {
+                       ksft_print_msg("Failed to get hctl for card %d: %s\n",
+                                      card, snd_strerror(err));
+                       goto next_card;
+               }
+
+               /* Count controls */
+               snd_ctl_elem_list_malloc(&card_data->ctls);
+               snd_ctl_elem_list(card_data->handle, card_data->ctls);
+               card_data->num_ctls = snd_ctl_elem_list_get_count(card_data->ctls);
+
+               /* Enumerate control information */
+               snd_ctl_elem_list_alloc_space(card_data->ctls, card_data->num_ctls);
+               snd_ctl_elem_list(card_data->handle, card_data->ctls);
+
+               card_data->card = num_cards++;
+               card_data->next = card_list;
+               card_list = card_data;
+
+               num_controls += card_data->num_ctls;
+
+               for (ctl = 0; ctl < card_data->num_ctls; ctl++) {
+                       ctl_data = malloc(sizeof(*ctl_data));
+                       if (!ctl_data)
+                               ksft_exit_fail_msg("Out of memory\n");
+
+                       ctl_data->card = card_data;
+                       ctl_data->elem = ctl;
+                       ctl_data->name = snd_ctl_elem_list_get_name(card_data->ctls,
+                                                                   ctl);
+
+                       err = snd_ctl_elem_id_malloc(&ctl_data->id);
+                       if (err < 0)
+                               ksft_exit_fail_msg("Out of memory\n");
+
+                       err = snd_ctl_elem_info_malloc(&ctl_data->info);
+                       if (err < 0)
+                               ksft_exit_fail_msg("Out of memory\n");
+
+                       err = snd_ctl_elem_value_malloc(&ctl_data->def_val);
+                       if (err < 0)
+                               ksft_exit_fail_msg("Out of memory\n");
+
+                       snd_ctl_elem_list_get_id(card_data->ctls, ctl,
+                                                ctl_data->id);
+                       snd_ctl_elem_info_set_id(ctl_data->info, ctl_data->id);
+                       err = snd_ctl_elem_info(card_data->handle,
+                                               ctl_data->info);
+                       if (err < 0) {
+                               ksft_print_msg("%s getting info for %d\n",
+                                              snd_strerror(err),
+                                              ctl_data->name);
+                       }
+
+                       snd_ctl_elem_value_set_id(ctl_data->def_val,
+                                                 ctl_data->id);
+
+                       ctl_data->next = ctl_list;
+                       ctl_list = ctl_data;
+               }
+
+       next_card:
+               if (snd_card_next(&card) < 0) {
+                       ksft_print_msg("snd_card_next");
+                       break;
+               }
+       }
+
+       snd_config_delete(config);
+}
+
+bool ctl_value_index_valid(struct ctl_data *ctl, snd_ctl_elem_value_t *val,
+                          int index)
+{
+       long int_val;
+       long long int64_val;
+
+       switch (snd_ctl_elem_info_get_type(ctl->info)) {
+       case SND_CTL_ELEM_TYPE_NONE:
+               ksft_print_msg("%s.%d Invalid control type NONE\n",
+                              ctl->name, index);
+               return false;
+
+       case SND_CTL_ELEM_TYPE_BOOLEAN:
+               int_val = snd_ctl_elem_value_get_boolean(val, index);
+               switch (int_val) {
+               case 0:
+               case 1:
+                       break;
+               default:
+                       ksft_print_msg("%s.%d Invalid boolean value %ld\n",
+                                      ctl->name, index, int_val);
+                       return false;
+               }
+               break;
+
+       case SND_CTL_ELEM_TYPE_INTEGER:
+               int_val = snd_ctl_elem_value_get_integer(val, index);
+
+               if (int_val < snd_ctl_elem_info_get_min(ctl->info)) {
+                       ksft_print_msg("%s.%d value %ld less than minimum %ld\n",
+                                      ctl->name, index, int_val,
+                                      snd_ctl_elem_info_get_min(ctl->info));
+                       return false;
+               }
+
+               if (int_val > snd_ctl_elem_info_get_max(ctl->info)) {
+                       ksft_print_msg("%s.%d value %ld more than maximum %ld\n",
+                                      ctl->name, index, int_val,
+                                      snd_ctl_elem_info_get_max(ctl->info));
+                       return false;
+               }
+
+               /* Only check step size if there is one and we're in bounds */
+               if (snd_ctl_elem_info_get_step(ctl->info) &&
+                   (int_val - snd_ctl_elem_info_get_min(ctl->info) %
+                    snd_ctl_elem_info_get_step(ctl->info))) {
+                       ksft_print_msg("%s.%d value %ld invalid for step %ld minimum %ld\n",
+                                      ctl->name, index, int_val,
+                                      snd_ctl_elem_info_get_step(ctl->info),
+                                      snd_ctl_elem_info_get_min(ctl->info));
+                       return false;
+               }
+               break;
+
+       case SND_CTL_ELEM_TYPE_INTEGER64:
+               int64_val = snd_ctl_elem_value_get_integer64(val, index);
+
+               if (int64_val < snd_ctl_elem_info_get_min64(ctl->info)) {
+                       ksft_print_msg("%s.%d value %lld less than minimum %lld\n",
+                                      ctl->name, index, int64_val,
+                                      snd_ctl_elem_info_get_min64(ctl->info));
+                       return false;
+               }
+
+               if (int64_val > snd_ctl_elem_info_get_max64(ctl->info)) {
+                       ksft_print_msg("%s.%d value %lld more than maximum %lld\n",
+                                      ctl->name, index, int64_val,
+                                      snd_ctl_elem_info_get_max(ctl->info));
+                       return false;
+               }
+
+               /* Only check step size if there is one and we're in bounds */
+               if (snd_ctl_elem_info_get_step64(ctl->info) &&
+                   (int64_val - snd_ctl_elem_info_get_min64(ctl->info)) %
+                   snd_ctl_elem_info_get_step64(ctl->info)) {
+                       ksft_print_msg("%s.%d value %lld invalid for step %lld minimum %lld\n",
+                                      ctl->name, index, int64_val,
+                                      snd_ctl_elem_info_get_step64(ctl->info),
+                                      snd_ctl_elem_info_get_min64(ctl->info));
+                       return false;
+               }
+               break;
+
+       case SND_CTL_ELEM_TYPE_ENUMERATED:
+               int_val = snd_ctl_elem_value_get_enumerated(val, index);
+
+               if (int_val < 0) {
+                       ksft_print_msg("%s.%d negative value %ld for enumeration\n",
+                                      ctl->name, index, int_val);
+                       return false;
+               }
+
+               if (int_val >= snd_ctl_elem_info_get_items(ctl->info)) {
+                       ksft_print_msg("%s.%d value %ld more than item count %ld\n",
+                                      ctl->name, index, int_val,
+                                      snd_ctl_elem_info_get_items(ctl->info));
+                       return false;
+               }
+               break;
+
+       default:
+               /* No tests for other types */
+               break;
+       }
+
+       return true;
+}
+
+/*
+ * Check that the provided value meets the constraints for the
+ * provided control.
+ */
+bool ctl_value_valid(struct ctl_data *ctl, snd_ctl_elem_value_t *val)
+{
+       int i;
+       bool valid = true;
+
+       for (i = 0; i < snd_ctl_elem_info_get_count(ctl->info); i++)
+               if (!ctl_value_index_valid(ctl, val, i))
+                       valid = false;
+
+       return valid;
+}
+
+/*
+ * Check that we can read the default value and it is valid. Write
+ * tests use the read value to restore the default.
+ */
+void test_ctl_get_value(struct ctl_data *ctl)
+{
+       int err;
+
+       /* If the control is turned off let's be polite */
+       if (snd_ctl_elem_info_is_inactive(ctl->info)) {
+               ksft_print_msg("%s is inactive\n", ctl->name);
+               ksft_test_result_skip("get_value.%d.%d\n",
+                                     ctl->card->card, ctl->elem);
+               return;
+       }
+
+       /* Can't test reading on an unreadable control */
+       if (!snd_ctl_elem_info_is_readable(ctl->info)) {
+               ksft_print_msg("%s is not readable\n", ctl->name);
+               ksft_test_result_skip("get_value.%d.%d\n",
+                                     ctl->card->card, ctl->elem);
+               return;
+       }
+
+       err = snd_ctl_elem_read(ctl->card->handle, ctl->def_val);
+       if (err < 0) {
+               ksft_print_msg("snd_ctl_elem_read() failed: %s\n",
+                              snd_strerror(err));
+               goto out;
+       }
+
+       if (!ctl_value_valid(ctl, ctl->def_val))
+               err = -EINVAL;
+
+out:
+       ksft_test_result(err >= 0, "get_value.%d.%d\n",
+                        ctl->card->card, ctl->elem);
+}
+
+bool show_mismatch(struct ctl_data *ctl, int index,
+                  snd_ctl_elem_value_t *read_val,
+                  snd_ctl_elem_value_t *expected_val)
+{
+       long long expected_int, read_int;
+
+       /*
+        * We factor out the code to compare values representable as
+        * integers, ensure that check doesn't log otherwise.
+        */
+       expected_int = 0;
+       read_int = 0;
+
+       switch (snd_ctl_elem_info_get_type(ctl->info)) {
+       case SND_CTL_ELEM_TYPE_BOOLEAN:
+               expected_int = snd_ctl_elem_value_get_boolean(expected_val,
+                                                             index);
+               read_int = snd_ctl_elem_value_get_boolean(read_val, index);
+               break;
+
+       case SND_CTL_ELEM_TYPE_INTEGER:
+               expected_int = snd_ctl_elem_value_get_integer(expected_val,
+                                                             index);
+               read_int = snd_ctl_elem_value_get_integer(read_val, index);
+               break;
+
+       case SND_CTL_ELEM_TYPE_INTEGER64:
+               expected_int = snd_ctl_elem_value_get_integer64(expected_val,
+                                                               index);
+               read_int = snd_ctl_elem_value_get_integer64(read_val,
+                                                           index);
+               break;
+
+       case SND_CTL_ELEM_TYPE_ENUMERATED:
+               expected_int = snd_ctl_elem_value_get_enumerated(expected_val,
+                                                                index);
+               read_int = snd_ctl_elem_value_get_enumerated(read_val,
+                                                            index);
+               break;
+
+       default:
+               break;
+       }
+
+       if (expected_int != read_int) {
+               /*
+                * NOTE: The volatile attribute means that the hardware
+                * can voluntarily change the state of control element
+                * independent of any operation by software.  
+                */
+               bool is_volatile = snd_ctl_elem_info_is_volatile(ctl->info);
+               ksft_print_msg("%s.%d expected %lld but read %lld, is_volatile %d\n",
+                              ctl->name, index, expected_int, read_int, is_volatile);
+               return !is_volatile;
+       } else {
+               return false;
+       }
+}
+
+/*
+ * Write a value then if possible verify that we get the expected
+ * result.  An optional expected value can be provided if we expect
+ * the write to fail, for verifying that invalid writes don't corrupt
+ * anything.
+ */
+int write_and_verify(struct ctl_data *ctl,
+                    snd_ctl_elem_value_t *write_val,
+                    snd_ctl_elem_value_t *expected_val)
+{
+       int err, i;
+       bool error_expected, mismatch_shown;
+       snd_ctl_elem_value_t *read_val, *w_val;
+       snd_ctl_elem_value_alloca(&read_val);
+       snd_ctl_elem_value_alloca(&w_val);
+
+       /*
+        * We need to copy the write value since writing can modify
+        * the value which causes surprises, and allocate an expected
+        * value if we expect to read back what we wrote.
+        */
+       snd_ctl_elem_value_copy(w_val, write_val);
+       if (expected_val) {
+               error_expected = true;
+       } else {
+               error_expected = false;
+               snd_ctl_elem_value_alloca(&expected_val);
+               snd_ctl_elem_value_copy(expected_val, write_val);
+       }
+
+       /*
+        * Do the write, if we have an expected value ignore the error
+        * and carry on to validate the expected value.
+        */
+       err = snd_ctl_elem_write(ctl->card->handle, w_val);
+       if (err < 0 && !error_expected) {
+               ksft_print_msg("snd_ctl_elem_write() failed: %s\n",
+                              snd_strerror(err));
+               return err;
+       }
+
+       /* Can we do the verification part? */
+       if (!snd_ctl_elem_info_is_readable(ctl->info))
+               return err;
+
+       snd_ctl_elem_value_set_id(read_val, ctl->id);
+
+       err = snd_ctl_elem_read(ctl->card->handle, read_val);
+       if (err < 0) {
+               ksft_print_msg("snd_ctl_elem_read() failed: %s\n",
+                              snd_strerror(err));
+               return err;
+       }
+
+       /*
+        * Use the libray to compare values, if there's a mismatch
+        * carry on and try to provide a more useful diagnostic than
+        * just "mismatch".
+        */
+       if (!snd_ctl_elem_value_compare(expected_val, read_val))
+               return 0;
+
+       mismatch_shown = false;
+       for (i = 0; i < snd_ctl_elem_info_get_count(ctl->info); i++)
+               if (show_mismatch(ctl, i, read_val, expected_val))
+                       mismatch_shown = true;
+
+       if (!mismatch_shown)
+               ksft_print_msg("%s read and written values differ\n",
+                              ctl->name);
+
+       return -1;
+}
+
+/*
+ * Make sure we can write the default value back to the control, this
+ * should validate that at least some write works.
+ */
+void test_ctl_write_default(struct ctl_data *ctl)
+{
+       int err;
+
+       /* If the control is turned off let's be polite */
+       if (snd_ctl_elem_info_is_inactive(ctl->info)) {
+               ksft_print_msg("%s is inactive\n", ctl->name);
+               ksft_test_result_skip("write_default.%d.%d\n",
+                                     ctl->card->card, ctl->elem);
+               return;
+       }
+
+       if (!snd_ctl_elem_info_is_writable(ctl->info)) {
+               ksft_print_msg("%s is not writeable\n", ctl->name);
+               ksft_test_result_skip("write_default.%d.%d\n",
+                                     ctl->card->card, ctl->elem);
+               return;
+       }
+
+       /* No idea what the default was for unreadable controls */
+       if (!snd_ctl_elem_info_is_readable(ctl->info)) {
+               ksft_print_msg("%s couldn't read default\n", ctl->name);
+               ksft_test_result_skip("write_default.%d.%d\n",
+                                     ctl->card->card, ctl->elem);
+               return;
+       }
+
+       err = write_and_verify(ctl, ctl->def_val, NULL);
+
+       ksft_test_result(err >= 0, "write_default.%d.%d\n",
+                        ctl->card->card, ctl->elem);
+}
+
+bool test_ctl_write_valid_boolean(struct ctl_data *ctl)
+{
+       int err, i, j;
+       bool fail = false;
+       snd_ctl_elem_value_t *val;
+       snd_ctl_elem_value_alloca(&val);
+
+       snd_ctl_elem_value_set_id(val, ctl->id);
+
+       for (i = 0; i < snd_ctl_elem_info_get_count(ctl->info); i++) {
+               for (j = 0; j < 2; j++) {
+                       snd_ctl_elem_value_set_boolean(val, i, j);
+                       err = write_and_verify(ctl, val, NULL);
+                       if (err != 0)
+                               fail = true;
+               }
+       }
+
+       return !fail;
+}
+
+bool test_ctl_write_valid_integer(struct ctl_data *ctl)
+{
+       int err;
+       int i;
+       long j, step;
+       bool fail = false;
+       snd_ctl_elem_value_t *val;
+       snd_ctl_elem_value_alloca(&val);
+
+       snd_ctl_elem_value_set_id(val, ctl->id);
+
+       step = snd_ctl_elem_info_get_step(ctl->info);
+       if (!step)
+               step = 1;
+
+       for (i = 0; i < snd_ctl_elem_info_get_count(ctl->info); i++) {
+               for (j = snd_ctl_elem_info_get_min(ctl->info);
+                    j <= snd_ctl_elem_info_get_max(ctl->info); j += step) {
+
+                       snd_ctl_elem_value_set_integer(val, i, j);
+                       err = write_and_verify(ctl, val, NULL);
+                       if (err != 0)
+                               fail = true;
+               }
+       }
+
+
+       return !fail;
+}
+
+bool test_ctl_write_valid_integer64(struct ctl_data *ctl)
+{
+       int err, i;
+       long long j, step;
+       bool fail = false;
+       snd_ctl_elem_value_t *val;
+       snd_ctl_elem_value_alloca(&val);
+
+       snd_ctl_elem_value_set_id(val, ctl->id);
+
+       step = snd_ctl_elem_info_get_step64(ctl->info);
+       if (!step)
+               step = 1;
+
+       for (i = 0; i < snd_ctl_elem_info_get_count(ctl->info); i++) {
+               for (j = snd_ctl_elem_info_get_min64(ctl->info);
+                    j <= snd_ctl_elem_info_get_max64(ctl->info); j += step) {
+
+                       snd_ctl_elem_value_set_integer64(val, i, j);
+                       err = write_and_verify(ctl, val, NULL);
+                       if (err != 0)
+                               fail = true;
+               }
+       }
+
+       return !fail;
+}
+
+bool test_ctl_write_valid_enumerated(struct ctl_data *ctl)
+{
+       int err, i, j;
+       bool fail = false;
+       snd_ctl_elem_value_t *val;
+       snd_ctl_elem_value_alloca(&val);
+
+       snd_ctl_elem_value_set_id(val, ctl->id);
+
+       for (i = 0; i < snd_ctl_elem_info_get_count(ctl->info); i++) {
+               for (j = 0; j < snd_ctl_elem_info_get_items(ctl->info); j++) {
+                       snd_ctl_elem_value_set_enumerated(val, i, j);
+                       err = write_and_verify(ctl, val, NULL);
+                       if (err != 0)
+                               fail = true;
+               }
+       }
+
+       return !fail;
+}
+
+void test_ctl_write_valid(struct ctl_data *ctl)
+{
+       bool pass;
+       int err;
+
+       /* If the control is turned off let's be polite */
+       if (snd_ctl_elem_info_is_inactive(ctl->info)) {
+               ksft_print_msg("%s is inactive\n", ctl->name);
+               ksft_test_result_skip("write_valid.%d.%d\n",
+                                     ctl->card->card, ctl->elem);
+               return;
+       }
+
+       if (!snd_ctl_elem_info_is_writable(ctl->info)) {
+               ksft_print_msg("%s is not writeable\n", ctl->name);
+               ksft_test_result_skip("write_valid.%d.%d\n",
+                                     ctl->card->card, ctl->elem);
+               return;
+       }
+
+       switch (snd_ctl_elem_info_get_type(ctl->info)) {
+       case SND_CTL_ELEM_TYPE_BOOLEAN:
+               pass = test_ctl_write_valid_boolean(ctl);
+               break;
+
+       case SND_CTL_ELEM_TYPE_INTEGER:
+               pass = test_ctl_write_valid_integer(ctl);
+               break;
+
+       case SND_CTL_ELEM_TYPE_INTEGER64:
+               pass = test_ctl_write_valid_integer64(ctl);
+               break;
+
+       case SND_CTL_ELEM_TYPE_ENUMERATED:
+               pass = test_ctl_write_valid_enumerated(ctl);
+               break;
+
+       default:
+               /* No tests for this yet */
+               ksft_test_result_skip("write_valid.%d.%d\n",
+                                     ctl->card->card, ctl->elem);
+               return;
+       }
+
+       /* Restore the default value to minimise disruption */
+       err = write_and_verify(ctl, ctl->def_val, NULL);
+       if (err < 0)
+               pass = false;
+
+       ksft_test_result(pass, "write_valid.%d.%d\n",
+                        ctl->card->card, ctl->elem);
+}
+
+int main(void)
+{
+       struct ctl_data *ctl;
+
+       ksft_print_header();
+
+       find_controls();
+
+       ksft_set_plan(num_controls * TESTS_PER_CONTROL);
+
+       for (ctl = ctl_list; ctl != NULL; ctl = ctl->next) {
+               /*
+                * Must test get_value() before we write anything, the
+                * test stores the default value for later cleanup.
+                */
+               test_ctl_get_value(ctl);
+               test_ctl_write_default(ctl);
+               test_ctl_write_valid(ctl);
+       }
+
+       ksft_exit_pass();
+
+       return 0;
+}