ALSA: hda: Move HD-audio core stuff into sound/hda/core
authorTakashi Iwai <tiwai@suse.de>
Wed, 9 Jul 2025 16:04:05 +0000 (18:04 +0200)
committerTakashi Iwai <tiwai@suse.de>
Fri, 11 Jul 2025 07:55:36 +0000 (09:55 +0200)
This is a part of HD-audio code restructuring.
Simply move the current code of sound/hda/* into the subdirectory
sound/hda/core, so that more stuff can be moved into sound/hda cleanly
later.

Most of file names with hdac_ and hdac_ext_ prefix are renamed without
the prefix, since they can be identified well in the directory name
and superfluous.

Signed-off-by: Takashi Iwai <tiwai@suse.de>
Link: https://patch.msgid.link/20250709160434.1859-3-tiwai@suse.de
46 files changed:
sound/hda/Kconfig
sound/hda/Makefile
sound/hda/array.c [deleted file]
sound/hda/core/Kconfig [new file with mode: 0644]
sound/hda/core/Makefile [new file with mode: 0644]
sound/hda/core/array.c [new file with mode: 0644]
sound/hda/core/bus.c [new file with mode: 0644]
sound/hda/core/component.c [new file with mode: 0644]
sound/hda/core/controller.c [new file with mode: 0644]
sound/hda/core/device.c [new file with mode: 0644]
sound/hda/core/ext/Makefile [new file with mode: 0644]
sound/hda/core/ext/bus.c [new file with mode: 0644]
sound/hda/core/ext/controller.c [new file with mode: 0644]
sound/hda/core/ext/stream.c [new file with mode: 0644]
sound/hda/core/hda_bus_type.c [new file with mode: 0644]
sound/hda/core/hdmi_chmap.c [new file with mode: 0644]
sound/hda/core/i915.c [new file with mode: 0644]
sound/hda/core/intel-dsp-config.c [new file with mode: 0644]
sound/hda/core/intel-nhlt.c [new file with mode: 0644]
sound/hda/core/intel-sdw-acpi.c [new file with mode: 0644]
sound/hda/core/local.h [new file with mode: 0644]
sound/hda/core/regmap.c [new file with mode: 0644]
sound/hda/core/stream.c [new file with mode: 0644]
sound/hda/core/sysfs.c [new file with mode: 0644]
sound/hda/core/trace.c [new file with mode: 0644]
sound/hda/core/trace.h [new file with mode: 0644]
sound/hda/ext/Makefile [deleted file]
sound/hda/ext/hdac_ext_bus.c [deleted file]
sound/hda/ext/hdac_ext_controller.c [deleted file]
sound/hda/ext/hdac_ext_stream.c [deleted file]
sound/hda/hda_bus_type.c [deleted file]
sound/hda/hdac_bus.c [deleted file]
sound/hda/hdac_component.c [deleted file]
sound/hda/hdac_controller.c [deleted file]
sound/hda/hdac_device.c [deleted file]
sound/hda/hdac_i915.c [deleted file]
sound/hda/hdac_regmap.c [deleted file]
sound/hda/hdac_stream.c [deleted file]
sound/hda/hdac_sysfs.c [deleted file]
sound/hda/hdmi_chmap.c [deleted file]
sound/hda/intel-dsp-config.c [deleted file]
sound/hda/intel-nhlt.c [deleted file]
sound/hda/intel-sdw-acpi.c [deleted file]
sound/hda/local.h [deleted file]
sound/hda/trace.c [deleted file]
sound/hda/trace.h [deleted file]

index eb488a522572464bc1e6c1a7da8fedea1fbaca82..e380146560772f0db18cb1a3c9812b4b275a4548 100644 (file)
@@ -1,67 +1,2 @@
 # SPDX-License-Identifier: GPL-2.0-only
-config SND_HDA_CORE
-       tristate
-       select REGMAP
-
-config SND_HDA_DSP_LOADER
-       bool
-
-config SND_HDA_ALIGNED_MMIO
-       bool
-
-config SND_HDA_COMPONENT
-       bool
-
-config SND_HDA_I915
-       bool
-       select SND_HDA_COMPONENT
-
-config SND_HDA_EXT_CORE
-       tristate
-       select SND_HDA_CORE
-
-config SND_HDA_PREALLOC_SIZE
-       int "Pre-allocated buffer size for HD-audio driver"
-       range 0 32768
-       default 0 if SND_DMA_SGBUF
-       default 64 if !SND_DMA_SGBUF
-       help
-         Specifies the default pre-allocated buffer-size in kB for the
-         HD-audio driver.  A larger buffer (e.g. 2048) is preferred
-         for systems using PulseAudio.  The default 64 is chosen just
-         for compatibility reasons.
-         On x86 systems, the default is zero as S/G allocation works
-         and no preallocation is needed in most cases.
-
-         Note that the pre-allocation size can be changed dynamically
-         via a proc file (/proc/asound/card*/pcm*/sub*/prealloc), too.
-
-config SND_INTEL_NHLT
-       bool
-       # this config should be selected only for Intel ACPI platforms.
-       # A fallback is provided so that the code compiles in all cases.
-
-config SND_INTEL_DSP_CONFIG
-       tristate
-       select ACPI_NHLT if ACPI
-       select SND_INTEL_NHLT if ACPI
-       select SND_INTEL_SOUNDWIRE_ACPI if ACPI
-       # this config should be selected only for Intel DSP platforms.
-       # A fallback is provided so that the code compiles in all cases.
-
-config SND_INTEL_SOUNDWIRE_ACPI
-       tristate
-
-config SND_INTEL_BYT_PREFER_SOF
-       bool "Prefer SOF driver over SST on BY/CHT platforms"
-       depends on SND_SST_ATOM_HIFI2_PLATFORM_ACPI && SND_SOC_SOF_BAYTRAIL
-       default n
-       help
-         The kernel has 2 drivers for the Low Power Engine audio-block on
-         Bay- and Cherry-Trail SoCs. The old SST driver and the new SOF
-         driver. If both drivers are enabled then the kernel will default
-         to using the old SST driver, unless told otherwise through the
-         snd_intel_dspcfg.dsp_driver module-parameter.
-
-         Set this option to Y to make the kernel default to the new SOF
-         driver instead.
+source "sound/hda/core/Kconfig"
index 83cceafe0d4c34668b41a8821bd90de857aca090..3fdbc22b153041bd514a9651d81312dc9f03dff1 100644 (file)
@@ -1,22 +1,2 @@
 # SPDX-License-Identifier: GPL-2.0
-snd-hda-core-y := hda_bus_type.o hdac_bus.o hdac_device.o hdac_sysfs.o \
-       hdac_regmap.o hdac_controller.o hdac_stream.o array.o hdmi_chmap.o
-
-snd-hda-core-y += trace.o
-CFLAGS_trace.o := -I$(src)
-
-# for sync with i915 gfx driver
-snd-hda-core-$(CONFIG_SND_HDA_COMPONENT) += hdac_component.o
-snd-hda-core-$(CONFIG_SND_HDA_I915) += hdac_i915.o
-
-obj-$(CONFIG_SND_HDA_CORE) += snd-hda-core.o
-
-#extended hda
-obj-$(CONFIG_SND_HDA_EXT_CORE) += ext/
-
-snd-intel-dspcfg-y := intel-dsp-config.o
-snd-intel-dspcfg-$(CONFIG_SND_INTEL_NHLT) += intel-nhlt.o
-obj-$(CONFIG_SND_INTEL_DSP_CONFIG) += snd-intel-dspcfg.o
-
-snd-intel-sdw-acpi-y := intel-sdw-acpi.o
-obj-$(CONFIG_SND_INTEL_SOUNDWIRE_ACPI) += snd-intel-sdw-acpi.o
+obj-y += core/
diff --git a/sound/hda/array.c b/sound/hda/array.c
deleted file mode 100644 (file)
index a204dce..0000000
+++ /dev/null
@@ -1,52 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0-only
-/*
- * generic arrays
- */
-
-#include <linux/slab.h>
-#include <sound/core.h>
-#include <sound/hdaudio.h>
-
-/**
- * snd_array_new - get a new element from the given array
- * @array: the array object
- *
- * Get a new element from the given array.  If it exceeds the
- * pre-allocated array size, re-allocate the array.
- *
- * Returns NULL if allocation failed.
- */
-void *snd_array_new(struct snd_array *array)
-{
-       if (snd_BUG_ON(!array->elem_size))
-               return NULL;
-       if (array->used >= array->alloced) {
-               int num = array->alloced + array->alloc_align;
-               int oldsize = array->alloced * array->elem_size;
-               int size = (num + 1) * array->elem_size;
-               void *nlist;
-               if (snd_BUG_ON(num >= 4096))
-                       return NULL;
-               nlist = krealloc(array->list, size, GFP_KERNEL);
-               if (!nlist)
-                       return NULL;
-               memset(nlist + oldsize, 0, size - oldsize);
-               array->list = nlist;
-               array->alloced = num;
-       }
-       return snd_array_elem(array, array->used++);
-}
-EXPORT_SYMBOL_GPL(snd_array_new);
-
-/**
- * snd_array_free - free the given array elements
- * @array: the array object
- */
-void snd_array_free(struct snd_array *array)
-{
-       kfree(array->list);
-       array->used = 0;
-       array->alloced = 0;
-       array->list = NULL;
-}
-EXPORT_SYMBOL_GPL(snd_array_free);
diff --git a/sound/hda/core/Kconfig b/sound/hda/core/Kconfig
new file mode 100644 (file)
index 0000000..eb488a5
--- /dev/null
@@ -0,0 +1,67 @@
+# SPDX-License-Identifier: GPL-2.0-only
+config SND_HDA_CORE
+       tristate
+       select REGMAP
+
+config SND_HDA_DSP_LOADER
+       bool
+
+config SND_HDA_ALIGNED_MMIO
+       bool
+
+config SND_HDA_COMPONENT
+       bool
+
+config SND_HDA_I915
+       bool
+       select SND_HDA_COMPONENT
+
+config SND_HDA_EXT_CORE
+       tristate
+       select SND_HDA_CORE
+
+config SND_HDA_PREALLOC_SIZE
+       int "Pre-allocated buffer size for HD-audio driver"
+       range 0 32768
+       default 0 if SND_DMA_SGBUF
+       default 64 if !SND_DMA_SGBUF
+       help
+         Specifies the default pre-allocated buffer-size in kB for the
+         HD-audio driver.  A larger buffer (e.g. 2048) is preferred
+         for systems using PulseAudio.  The default 64 is chosen just
+         for compatibility reasons.
+         On x86 systems, the default is zero as S/G allocation works
+         and no preallocation is needed in most cases.
+
+         Note that the pre-allocation size can be changed dynamically
+         via a proc file (/proc/asound/card*/pcm*/sub*/prealloc), too.
+
+config SND_INTEL_NHLT
+       bool
+       # this config should be selected only for Intel ACPI platforms.
+       # A fallback is provided so that the code compiles in all cases.
+
+config SND_INTEL_DSP_CONFIG
+       tristate
+       select ACPI_NHLT if ACPI
+       select SND_INTEL_NHLT if ACPI
+       select SND_INTEL_SOUNDWIRE_ACPI if ACPI
+       # this config should be selected only for Intel DSP platforms.
+       # A fallback is provided so that the code compiles in all cases.
+
+config SND_INTEL_SOUNDWIRE_ACPI
+       tristate
+
+config SND_INTEL_BYT_PREFER_SOF
+       bool "Prefer SOF driver over SST on BY/CHT platforms"
+       depends on SND_SST_ATOM_HIFI2_PLATFORM_ACPI && SND_SOC_SOF_BAYTRAIL
+       default n
+       help
+         The kernel has 2 drivers for the Low Power Engine audio-block on
+         Bay- and Cherry-Trail SoCs. The old SST driver and the new SOF
+         driver. If both drivers are enabled then the kernel will default
+         to using the old SST driver, unless told otherwise through the
+         snd_intel_dspcfg.dsp_driver module-parameter.
+
+         Set this option to Y to make the kernel default to the new SOF
+         driver instead.
diff --git a/sound/hda/core/Makefile b/sound/hda/core/Makefile
new file mode 100644 (file)
index 0000000..89cb461
--- /dev/null
@@ -0,0 +1,22 @@
+# SPDX-License-Identifier: GPL-2.0
+snd-hda-core-y := hda_bus_type.o bus.o device.o sysfs.o \
+       regmap.o controller.o stream.o array.o hdmi_chmap.o
+
+snd-hda-core-y += trace.o
+CFLAGS_trace.o := -I$(src)
+
+# for sync with i915 gfx driver
+snd-hda-core-$(CONFIG_SND_HDA_COMPONENT) += component.o
+snd-hda-core-$(CONFIG_SND_HDA_I915) += i915.o
+
+obj-$(CONFIG_SND_HDA_CORE) += snd-hda-core.o
+
+#extended hda
+obj-$(CONFIG_SND_HDA_EXT_CORE) += ext/
+
+snd-intel-dspcfg-y := intel-dsp-config.o
+snd-intel-dspcfg-$(CONFIG_SND_INTEL_NHLT) += intel-nhlt.o
+obj-$(CONFIG_SND_INTEL_DSP_CONFIG) += snd-intel-dspcfg.o
+
+snd-intel-sdw-acpi-y := intel-sdw-acpi.o
+obj-$(CONFIG_SND_INTEL_SOUNDWIRE_ACPI) += snd-intel-sdw-acpi.o
diff --git a/sound/hda/core/array.c b/sound/hda/core/array.c
new file mode 100644 (file)
index 0000000..a204dce
--- /dev/null
@@ -0,0 +1,52 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * generic arrays
+ */
+
+#include <linux/slab.h>
+#include <sound/core.h>
+#include <sound/hdaudio.h>
+
+/**
+ * snd_array_new - get a new element from the given array
+ * @array: the array object
+ *
+ * Get a new element from the given array.  If it exceeds the
+ * pre-allocated array size, re-allocate the array.
+ *
+ * Returns NULL if allocation failed.
+ */
+void *snd_array_new(struct snd_array *array)
+{
+       if (snd_BUG_ON(!array->elem_size))
+               return NULL;
+       if (array->used >= array->alloced) {
+               int num = array->alloced + array->alloc_align;
+               int oldsize = array->alloced * array->elem_size;
+               int size = (num + 1) * array->elem_size;
+               void *nlist;
+               if (snd_BUG_ON(num >= 4096))
+                       return NULL;
+               nlist = krealloc(array->list, size, GFP_KERNEL);
+               if (!nlist)
+                       return NULL;
+               memset(nlist + oldsize, 0, size - oldsize);
+               array->list = nlist;
+               array->alloced = num;
+       }
+       return snd_array_elem(array, array->used++);
+}
+EXPORT_SYMBOL_GPL(snd_array_new);
+
+/**
+ * snd_array_free - free the given array elements
+ * @array: the array object
+ */
+void snd_array_free(struct snd_array *array)
+{
+       kfree(array->list);
+       array->used = 0;
+       array->alloced = 0;
+       array->list = NULL;
+}
+EXPORT_SYMBOL_GPL(snd_array_free);
diff --git a/sound/hda/core/bus.c b/sound/hda/core/bus.c
new file mode 100644 (file)
index 0000000..d497414
--- /dev/null
@@ -0,0 +1,289 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * HD-audio core bus driver
+ */
+
+#include <linux/init.h>
+#include <linux/io.h>
+#include <linux/device.h>
+#include <linux/module.h>
+#include <linux/export.h>
+#include <sound/hdaudio.h>
+#include "local.h"
+#include "trace.h"
+
+static void snd_hdac_bus_process_unsol_events(struct work_struct *work);
+
+static const struct hdac_bus_ops default_ops = {
+       .command = snd_hdac_bus_send_cmd,
+       .get_response = snd_hdac_bus_get_response,
+       .link_power = snd_hdac_bus_link_power,
+};
+
+/**
+ * snd_hdac_bus_init - initialize a HD-audio bas bus
+ * @bus: the pointer to bus object
+ * @dev: device pointer
+ * @ops: bus verb operators
+ *
+ * Returns 0 if successful, or a negative error code.
+ */
+int snd_hdac_bus_init(struct hdac_bus *bus, struct device *dev,
+                     const struct hdac_bus_ops *ops)
+{
+       memset(bus, 0, sizeof(*bus));
+       bus->dev = dev;
+       if (ops)
+               bus->ops = ops;
+       else
+               bus->ops = &default_ops;
+       bus->dma_type = SNDRV_DMA_TYPE_DEV;
+       INIT_LIST_HEAD(&bus->stream_list);
+       INIT_LIST_HEAD(&bus->codec_list);
+       INIT_WORK(&bus->unsol_work, snd_hdac_bus_process_unsol_events);
+       spin_lock_init(&bus->reg_lock);
+       mutex_init(&bus->cmd_mutex);
+       mutex_init(&bus->lock);
+       INIT_LIST_HEAD(&bus->hlink_list);
+       init_waitqueue_head(&bus->rirb_wq);
+       bus->irq = -1;
+
+       /*
+        * Default value of '8' is as per the HD audio specification (Rev 1.0a).
+        * Following relation is used to derive STRIPE control value.
+        *  For sample rate <= 48K:
+        *   { ((num_channels * bits_per_sample) / number of SDOs) >= 8 }
+        *  For sample rate > 48K:
+        *   { ((num_channels * bits_per_sample * rate/48000) /
+        *      number of SDOs) >= 8 }
+        */
+       bus->sdo_limit = 8;
+
+       return 0;
+}
+EXPORT_SYMBOL_GPL(snd_hdac_bus_init);
+
+/**
+ * snd_hdac_bus_exit - clean up a HD-audio bas bus
+ * @bus: the pointer to bus object
+ */
+void snd_hdac_bus_exit(struct hdac_bus *bus)
+{
+       WARN_ON(!list_empty(&bus->stream_list));
+       WARN_ON(!list_empty(&bus->codec_list));
+       cancel_work_sync(&bus->unsol_work);
+}
+EXPORT_SYMBOL_GPL(snd_hdac_bus_exit);
+
+/**
+ * snd_hdac_bus_exec_verb - execute a HD-audio verb on the given bus
+ * @bus: bus object
+ * @addr: the HDAC device address
+ * @cmd: HD-audio encoded verb
+ * @res: pointer to store the response, NULL if performing asynchronously
+ *
+ * Returns 0 if successful, or a negative error code.
+ */
+int snd_hdac_bus_exec_verb(struct hdac_bus *bus, unsigned int addr,
+                          unsigned int cmd, unsigned int *res)
+{
+       int err;
+
+       mutex_lock(&bus->cmd_mutex);
+       err = snd_hdac_bus_exec_verb_unlocked(bus, addr, cmd, res);
+       mutex_unlock(&bus->cmd_mutex);
+       return err;
+}
+
+/**
+ * snd_hdac_bus_exec_verb_unlocked - unlocked version
+ * @bus: bus object
+ * @addr: the HDAC device address
+ * @cmd: HD-audio encoded verb
+ * @res: pointer to store the response, NULL if performing asynchronously
+ *
+ * Returns 0 if successful, or a negative error code.
+ */
+int snd_hdac_bus_exec_verb_unlocked(struct hdac_bus *bus, unsigned int addr,
+                                   unsigned int cmd, unsigned int *res)
+{
+       unsigned int tmp;
+       int err;
+
+       if (cmd == ~0)
+               return -EINVAL;
+
+       if (res)
+               *res = -1;
+       else if (bus->sync_write)
+               res = &tmp;
+       for (;;) {
+               trace_hda_send_cmd(bus, cmd);
+               err = bus->ops->command(bus, cmd);
+               if (err != -EAGAIN)
+                       break;
+               /* process pending verbs */
+               err = bus->ops->get_response(bus, addr, &tmp);
+               if (err)
+                       break;
+       }
+       if (!err && res) {
+               err = bus->ops->get_response(bus, addr, res);
+               trace_hda_get_response(bus, addr, *res);
+       }
+       return err;
+}
+EXPORT_SYMBOL_GPL(snd_hdac_bus_exec_verb_unlocked);
+
+/**
+ * snd_hdac_bus_queue_event - add an unsolicited event to queue
+ * @bus: the BUS
+ * @res: unsolicited event (lower 32bit of RIRB entry)
+ * @res_ex: codec addr and flags (upper 32bit or RIRB entry)
+ *
+ * Adds the given event to the queue.  The events are processed in
+ * the workqueue asynchronously.  Call this function in the interrupt
+ * hanlder when RIRB receives an unsolicited event.
+ */
+void snd_hdac_bus_queue_event(struct hdac_bus *bus, u32 res, u32 res_ex)
+{
+       unsigned int wp;
+
+       if (!bus)
+               return;
+
+       trace_hda_unsol_event(bus, res, res_ex);
+       wp = (bus->unsol_wp + 1) % HDA_UNSOL_QUEUE_SIZE;
+       bus->unsol_wp = wp;
+
+       wp <<= 1;
+       bus->unsol_queue[wp] = res;
+       bus->unsol_queue[wp + 1] = res_ex;
+
+       schedule_work(&bus->unsol_work);
+}
+
+/*
+ * process queued unsolicited events
+ */
+static void snd_hdac_bus_process_unsol_events(struct work_struct *work)
+{
+       struct hdac_bus *bus = container_of(work, struct hdac_bus, unsol_work);
+       struct hdac_device *codec;
+       struct hdac_driver *drv;
+       unsigned int rp, caddr, res;
+
+       spin_lock_irq(&bus->reg_lock);
+       while (bus->unsol_rp != bus->unsol_wp) {
+               rp = (bus->unsol_rp + 1) % HDA_UNSOL_QUEUE_SIZE;
+               bus->unsol_rp = rp;
+               rp <<= 1;
+               res = bus->unsol_queue[rp];
+               caddr = bus->unsol_queue[rp + 1];
+               if (!(caddr & (1 << 4))) /* no unsolicited event? */
+                       continue;
+               codec = bus->caddr_tbl[caddr & 0x0f];
+               if (!codec || !codec->registered)
+                       continue;
+               spin_unlock_irq(&bus->reg_lock);
+               drv = drv_to_hdac_driver(codec->dev.driver);
+               if (drv->unsol_event)
+                       drv->unsol_event(codec, res);
+               spin_lock_irq(&bus->reg_lock);
+       }
+       spin_unlock_irq(&bus->reg_lock);
+}
+
+/**
+ * snd_hdac_bus_add_device - Add a codec to bus
+ * @bus: HDA core bus
+ * @codec: HDA core device to add
+ *
+ * Adds the given codec to the list in the bus.  The caddr_tbl array
+ * and codec_powered bits are updated, as well.
+ * Returns zero if success, or a negative error code.
+ */
+int snd_hdac_bus_add_device(struct hdac_bus *bus, struct hdac_device *codec)
+{
+       if (bus->caddr_tbl[codec->addr]) {
+               dev_err(bus->dev, "address 0x%x is already occupied\n",
+                       codec->addr);
+               return -EBUSY;
+       }
+
+       list_add_tail(&codec->list, &bus->codec_list);
+       bus->caddr_tbl[codec->addr] = codec;
+       set_bit(codec->addr, &bus->codec_powered);
+       bus->num_codecs++;
+       return 0;
+}
+
+/**
+ * snd_hdac_bus_remove_device - Remove a codec from bus
+ * @bus: HDA core bus
+ * @codec: HDA core device to remove
+ */
+void snd_hdac_bus_remove_device(struct hdac_bus *bus,
+                               struct hdac_device *codec)
+{
+       WARN_ON(bus != codec->bus);
+       if (list_empty(&codec->list))
+               return;
+       list_del_init(&codec->list);
+       bus->caddr_tbl[codec->addr] = NULL;
+       clear_bit(codec->addr, &bus->codec_powered);
+       bus->num_codecs--;
+       flush_work(&bus->unsol_work);
+}
+
+#ifdef CONFIG_SND_HDA_ALIGNED_MMIO
+/* Helpers for aligned read/write of mmio space, for Tegra */
+unsigned int snd_hdac_aligned_read(void __iomem *addr, unsigned int mask)
+{
+       void __iomem *aligned_addr =
+               (void __iomem *)((unsigned long)(addr) & ~0x3);
+       unsigned int shift = ((unsigned long)(addr) & 0x3) << 3;
+       unsigned int v;
+
+       v = readl(aligned_addr);
+       return (v >> shift) & mask;
+}
+EXPORT_SYMBOL_GPL(snd_hdac_aligned_read);
+
+void snd_hdac_aligned_write(unsigned int val, void __iomem *addr,
+                           unsigned int mask)
+{
+       void __iomem *aligned_addr =
+               (void __iomem *)((unsigned long)(addr) & ~0x3);
+       unsigned int shift = ((unsigned long)(addr) & 0x3) << 3;
+       unsigned int v;
+
+       v = readl(aligned_addr);
+       v &= ~(mask << shift);
+       v |= val << shift;
+       writel(v, aligned_addr);
+}
+EXPORT_SYMBOL_GPL(snd_hdac_aligned_write);
+#endif /* CONFIG_SND_HDA_ALIGNED_MMIO */
+
+void snd_hdac_codec_link_up(struct hdac_device *codec)
+{
+       struct hdac_bus *bus = codec->bus;
+
+       if (bus->ops->link_power)
+               bus->ops->link_power(codec, true);
+       else
+               snd_hdac_bus_link_power(codec, true);
+}
+EXPORT_SYMBOL_GPL(snd_hdac_codec_link_up);
+
+void snd_hdac_codec_link_down(struct hdac_device *codec)
+{
+       struct hdac_bus *bus = codec->bus;
+
+       if (bus->ops->link_power)
+               bus->ops->link_power(codec, false);
+       else
+               snd_hdac_bus_link_power(codec, false);
+}
+EXPORT_SYMBOL_GPL(snd_hdac_codec_link_down);
diff --git a/sound/hda/core/component.c b/sound/hda/core/component.c
new file mode 100644 (file)
index 0000000..9c82a28
--- /dev/null
@@ -0,0 +1,353 @@
+// SPDX-License-Identifier: GPL-2.0
+// hdac_component.c - routines for sync between HD-A core and DRM driver
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/pci.h>
+#include <linux/component.h>
+#include <linux/string_choices.h>
+#include <sound/core.h>
+#include <sound/hdaudio.h>
+#include <sound/hda_component.h>
+#include <sound/hda_register.h>
+
+static void hdac_acomp_release(struct device *dev, void *res)
+{
+}
+
+static struct drm_audio_component *hdac_get_acomp(struct device *dev)
+{
+       return devres_find(dev, hdac_acomp_release, NULL, NULL);
+}
+
+/**
+ * snd_hdac_set_codec_wakeup - Enable / disable HDMI/DP codec wakeup
+ * @bus: HDA core bus
+ * @enable: enable or disable the wakeup
+ *
+ * This function is supposed to be used only by a HD-audio controller
+ * driver that needs the interaction with graphics driver.
+ *
+ * This function should be called during the chip reset, also called at
+ * resume for updating STATESTS register read.
+ *
+ * Returns zero for success or a negative error code.
+ */
+int snd_hdac_set_codec_wakeup(struct hdac_bus *bus, bool enable)
+{
+       struct drm_audio_component *acomp = bus->audio_component;
+
+       if (!acomp || !acomp->ops)
+               return -ENODEV;
+
+       if (!acomp->ops->codec_wake_override)
+               return 0;
+
+       dev_dbg(bus->dev, "%s codec wakeup\n", str_enable_disable(enable));
+
+       acomp->ops->codec_wake_override(acomp->dev, enable);
+
+       return 0;
+}
+EXPORT_SYMBOL_GPL(snd_hdac_set_codec_wakeup);
+
+/**
+ * snd_hdac_display_power - Power up / down the power refcount
+ * @bus: HDA core bus
+ * @idx: HDA codec address, pass HDA_CODEC_IDX_CONTROLLER for controller
+ * @enable: power up or down
+ *
+ * This function is used by either HD-audio controller or codec driver that
+ * needs the interaction with graphics driver.
+ *
+ * This function updates the power status, and calls the get_power() and
+ * put_power() ops accordingly, toggling the codec wakeup, too.
+ */
+void snd_hdac_display_power(struct hdac_bus *bus, unsigned int idx, bool enable)
+{
+       struct drm_audio_component *acomp = bus->audio_component;
+
+       dev_dbg(bus->dev, "display power %s\n", str_enable_disable(enable));
+
+       mutex_lock(&bus->lock);
+       if (enable)
+               set_bit(idx, &bus->display_power_status);
+       else
+               clear_bit(idx, &bus->display_power_status);
+
+       if (!acomp || !acomp->ops)
+               goto unlock;
+
+       if (bus->display_power_status) {
+               if (!bus->display_power_active) {
+                       unsigned long cookie = -1;
+
+                       if (acomp->ops->get_power)
+                               cookie = acomp->ops->get_power(acomp->dev);
+
+                       snd_hdac_set_codec_wakeup(bus, true);
+                       snd_hdac_set_codec_wakeup(bus, false);
+                       bus->display_power_active = cookie;
+               }
+       } else {
+               if (bus->display_power_active) {
+                       unsigned long cookie = bus->display_power_active;
+
+                       if (acomp->ops->put_power)
+                               acomp->ops->put_power(acomp->dev, cookie);
+
+                       bus->display_power_active = 0;
+               }
+       }
+ unlock:
+       mutex_unlock(&bus->lock);
+}
+EXPORT_SYMBOL_GPL(snd_hdac_display_power);
+
+/**
+ * snd_hdac_sync_audio_rate - Set N/CTS based on the sample rate
+ * @codec: HDA codec
+ * @nid: the pin widget NID
+ * @dev_id: device identifier
+ * @rate: the sample rate to set
+ *
+ * This function is supposed to be used only by a HD-audio controller
+ * driver that needs the interaction with graphics driver.
+ *
+ * This function sets N/CTS value based on the given sample rate.
+ * Returns zero for success, or a negative error code.
+ */
+int snd_hdac_sync_audio_rate(struct hdac_device *codec, hda_nid_t nid,
+                            int dev_id, int rate)
+{
+       struct hdac_bus *bus = codec->bus;
+       struct drm_audio_component *acomp = bus->audio_component;
+       int port, pipe;
+
+       if (!acomp || !acomp->ops || !acomp->ops->sync_audio_rate)
+               return -ENODEV;
+       port = nid;
+       if (acomp->audio_ops && acomp->audio_ops->pin2port) {
+               port = acomp->audio_ops->pin2port(codec, nid);
+               if (port < 0)
+                       return -EINVAL;
+       }
+       pipe = dev_id;
+       return acomp->ops->sync_audio_rate(acomp->dev, port, pipe, rate);
+}
+EXPORT_SYMBOL_GPL(snd_hdac_sync_audio_rate);
+
+/**
+ * snd_hdac_acomp_get_eld - Get the audio state and ELD via component
+ * @codec: HDA codec
+ * @nid: the pin widget NID
+ * @dev_id: device identifier
+ * @audio_enabled: the pointer to store the current audio state
+ * @buffer: the buffer pointer to store ELD bytes
+ * @max_bytes: the max bytes to be stored on @buffer
+ *
+ * This function is supposed to be used only by a HD-audio controller
+ * driver that needs the interaction with graphics driver.
+ *
+ * This function queries the current state of the audio on the given
+ * digital port and fetches the ELD bytes onto the given buffer.
+ * It returns the number of bytes for the total ELD data, zero for
+ * invalid ELD, or a negative error code.
+ *
+ * The return size is the total bytes required for the whole ELD bytes,
+ * thus it may be over @max_bytes.  If it's over @max_bytes, it implies
+ * that only a part of ELD bytes have been fetched.
+ */
+int snd_hdac_acomp_get_eld(struct hdac_device *codec, hda_nid_t nid, int dev_id,
+                          bool *audio_enabled, char *buffer, int max_bytes)
+{
+       struct hdac_bus *bus = codec->bus;
+       struct drm_audio_component *acomp = bus->audio_component;
+       int port, pipe;
+
+       if (!acomp || !acomp->ops || !acomp->ops->get_eld)
+               return -ENODEV;
+
+       port = nid;
+       if (acomp->audio_ops && acomp->audio_ops->pin2port) {
+               port = acomp->audio_ops->pin2port(codec, nid);
+               if (port < 0)
+                       return -EINVAL;
+       }
+       pipe = dev_id;
+       return acomp->ops->get_eld(acomp->dev, port, pipe, audio_enabled,
+                                  buffer, max_bytes);
+}
+EXPORT_SYMBOL_GPL(snd_hdac_acomp_get_eld);
+
+static int hdac_component_master_bind(struct device *dev)
+{
+       struct drm_audio_component *acomp = hdac_get_acomp(dev);
+       int ret;
+
+       if (WARN_ON(!acomp))
+               return -EINVAL;
+
+       ret = component_bind_all(dev, acomp);
+       if (ret < 0)
+               return ret;
+
+       if (WARN_ON(!(acomp->dev && acomp->ops))) {
+               ret = -EINVAL;
+               goto out_unbind;
+       }
+
+       /* pin the module to avoid dynamic unbinding, but only if given */
+       if (!try_module_get(acomp->ops->owner)) {
+               ret = -ENODEV;
+               goto out_unbind;
+       }
+
+       if (acomp->audio_ops && acomp->audio_ops->master_bind) {
+               ret = acomp->audio_ops->master_bind(dev, acomp);
+               if (ret < 0)
+                       goto module_put;
+       }
+
+       complete_all(&acomp->master_bind_complete);
+       return 0;
+
+ module_put:
+       module_put(acomp->ops->owner);
+out_unbind:
+       component_unbind_all(dev, acomp);
+       complete_all(&acomp->master_bind_complete);
+
+       return ret;
+}
+
+static void hdac_component_master_unbind(struct device *dev)
+{
+       struct drm_audio_component *acomp = hdac_get_acomp(dev);
+
+       if (acomp->audio_ops && acomp->audio_ops->master_unbind)
+               acomp->audio_ops->master_unbind(dev, acomp);
+       module_put(acomp->ops->owner);
+       component_unbind_all(dev, acomp);
+       WARN_ON(acomp->ops || acomp->dev);
+}
+
+static const struct component_master_ops hdac_component_master_ops = {
+       .bind = hdac_component_master_bind,
+       .unbind = hdac_component_master_unbind,
+};
+
+/**
+ * snd_hdac_acomp_register_notifier - Register audio component ops
+ * @bus: HDA core bus
+ * @aops: audio component ops
+ *
+ * This function is supposed to be used only by a HD-audio controller
+ * driver that needs the interaction with graphics driver.
+ *
+ * This function sets the given ops to be called by the graphics driver.
+ *
+ * Returns zero for success or a negative error code.
+ */
+int snd_hdac_acomp_register_notifier(struct hdac_bus *bus,
+                                   const struct drm_audio_component_audio_ops *aops)
+{
+       if (!bus->audio_component)
+               return -ENODEV;
+
+       bus->audio_component->audio_ops = aops;
+       return 0;
+}
+EXPORT_SYMBOL_GPL(snd_hdac_acomp_register_notifier);
+
+/**
+ * snd_hdac_acomp_init - Initialize audio component
+ * @bus: HDA core bus
+ * @aops: audio component ops
+ * @match_master: match function for finding components
+ * @extra_size: Extra bytes to allocate
+ *
+ * This function is supposed to be used only by a HD-audio controller
+ * driver that needs the interaction with graphics driver.
+ *
+ * This function initializes and sets up the audio component to communicate
+ * with graphics driver.
+ *
+ * Unlike snd_hdac_i915_init(), this function doesn't synchronize with the
+ * binding with the DRM component.  Each caller needs to sync via master_bind
+ * audio_ops.
+ *
+ * Returns zero for success or a negative error code.
+ */
+int snd_hdac_acomp_init(struct hdac_bus *bus,
+                       const struct drm_audio_component_audio_ops *aops,
+                       int (*match_master)(struct device *, int, void *),
+                       size_t extra_size)
+{
+       struct component_match *match = NULL;
+       struct device *dev = bus->dev;
+       struct drm_audio_component *acomp;
+       int ret;
+
+       if (WARN_ON(hdac_get_acomp(dev)))
+               return -EBUSY;
+
+       acomp = devres_alloc(hdac_acomp_release, sizeof(*acomp) + extra_size,
+                            GFP_KERNEL);
+       if (!acomp)
+               return -ENOMEM;
+       acomp->audio_ops = aops;
+       init_completion(&acomp->master_bind_complete);
+       bus->audio_component = acomp;
+       devres_add(dev, acomp);
+
+       component_match_add_typed(dev, &match, match_master, bus);
+       ret = component_master_add_with_match(dev, &hdac_component_master_ops,
+                                             match);
+       if (ret < 0)
+               goto out_err;
+
+       return 0;
+
+out_err:
+       bus->audio_component = NULL;
+       devres_destroy(dev, hdac_acomp_release, NULL, NULL);
+       dev_info(dev, "failed to add audio component master (%d)\n", ret);
+
+       return ret;
+}
+EXPORT_SYMBOL_GPL(snd_hdac_acomp_init);
+
+/**
+ * snd_hdac_acomp_exit - Finalize audio component
+ * @bus: HDA core bus
+ *
+ * This function is supposed to be used only by a HD-audio controller
+ * driver that needs the interaction with graphics driver.
+ *
+ * This function releases the audio component that has been used.
+ *
+ * Returns zero for success or a negative error code.
+ */
+int snd_hdac_acomp_exit(struct hdac_bus *bus)
+{
+       struct device *dev = bus->dev;
+       struct drm_audio_component *acomp = bus->audio_component;
+
+       if (!acomp)
+               return 0;
+
+       if (WARN_ON(bus->display_power_active) && acomp->ops)
+               acomp->ops->put_power(acomp->dev, bus->display_power_active);
+
+       bus->display_power_active = 0;
+       bus->display_power_status = 0;
+
+       component_master_del(dev, &hdac_component_master_ops);
+
+       bus->audio_component = NULL;
+       devres_destroy(dev, hdac_acomp_release, NULL, NULL);
+
+       return 0;
+}
+EXPORT_SYMBOL_GPL(snd_hdac_acomp_exit);
diff --git a/sound/hda/core/controller.c b/sound/hda/core/controller.c
new file mode 100644 (file)
index 0000000..b5c833b
--- /dev/null
@@ -0,0 +1,778 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * HD-audio controller helpers
+ */
+
+#include <linux/kernel.h>
+#include <linux/delay.h>
+#include <linux/export.h>
+#include <sound/core.h>
+#include <sound/hdaudio.h>
+#include <sound/hda_register.h>
+#include "local.h"
+
+/* clear CORB read pointer properly */
+static void azx_clear_corbrp(struct hdac_bus *bus)
+{
+       int timeout;
+
+       for (timeout = 1000; timeout > 0; timeout--) {
+               if (snd_hdac_chip_readw(bus, CORBRP) & AZX_CORBRP_RST)
+                       break;
+               udelay(1);
+       }
+       if (timeout <= 0)
+               dev_err(bus->dev, "CORB reset timeout#1, CORBRP = %d\n",
+                       snd_hdac_chip_readw(bus, CORBRP));
+
+       snd_hdac_chip_writew(bus, CORBRP, 0);
+       for (timeout = 1000; timeout > 0; timeout--) {
+               if (snd_hdac_chip_readw(bus, CORBRP) == 0)
+                       break;
+               udelay(1);
+       }
+       if (timeout <= 0)
+               dev_err(bus->dev, "CORB reset timeout#2, CORBRP = %d\n",
+                       snd_hdac_chip_readw(bus, CORBRP));
+}
+
+/**
+ * snd_hdac_bus_init_cmd_io - set up CORB/RIRB buffers
+ * @bus: HD-audio core bus
+ */
+void snd_hdac_bus_init_cmd_io(struct hdac_bus *bus)
+{
+       WARN_ON_ONCE(!bus->rb.area);
+
+       spin_lock_irq(&bus->reg_lock);
+       /* CORB set up */
+       bus->corb.addr = bus->rb.addr;
+       bus->corb.buf = (__le32 *)bus->rb.area;
+       snd_hdac_chip_writel(bus, CORBLBASE, (u32)bus->corb.addr);
+       snd_hdac_chip_writel(bus, CORBUBASE, upper_32_bits(bus->corb.addr));
+
+       /* set the corb size to 256 entries (ULI requires explicitly) */
+       snd_hdac_chip_writeb(bus, CORBSIZE, 0x02);
+       /* set the corb write pointer to 0 */
+       snd_hdac_chip_writew(bus, CORBWP, 0);
+
+       /* reset the corb hw read pointer */
+       snd_hdac_chip_writew(bus, CORBRP, AZX_CORBRP_RST);
+       if (!bus->corbrp_self_clear)
+               azx_clear_corbrp(bus);
+
+       /* enable corb dma */
+       if (!bus->use_pio_for_commands)
+               snd_hdac_chip_writeb(bus, CORBCTL, AZX_CORBCTL_RUN);
+
+       /* RIRB set up */
+       bus->rirb.addr = bus->rb.addr + 2048;
+       bus->rirb.buf = (__le32 *)(bus->rb.area + 2048);
+       bus->rirb.wp = bus->rirb.rp = 0;
+       memset(bus->rirb.cmds, 0, sizeof(bus->rirb.cmds));
+       snd_hdac_chip_writel(bus, RIRBLBASE, (u32)bus->rirb.addr);
+       snd_hdac_chip_writel(bus, RIRBUBASE, upper_32_bits(bus->rirb.addr));
+
+       /* set the rirb size to 256 entries (ULI requires explicitly) */
+       snd_hdac_chip_writeb(bus, RIRBSIZE, 0x02);
+       /* reset the rirb hw write pointer */
+       snd_hdac_chip_writew(bus, RIRBWP, AZX_RIRBWP_RST);
+       /* set N=1, get RIRB response interrupt for new entry */
+       snd_hdac_chip_writew(bus, RINTCNT, 1);
+       /* enable rirb dma and response irq */
+       if (bus->not_use_interrupts)
+               snd_hdac_chip_writeb(bus, RIRBCTL, AZX_RBCTL_DMA_EN);
+       else
+               snd_hdac_chip_writeb(bus, RIRBCTL, AZX_RBCTL_DMA_EN | AZX_RBCTL_IRQ_EN);
+       /* Accept unsolicited responses */
+       snd_hdac_chip_updatel(bus, GCTL, AZX_GCTL_UNSOL, AZX_GCTL_UNSOL);
+       spin_unlock_irq(&bus->reg_lock);
+}
+EXPORT_SYMBOL_GPL(snd_hdac_bus_init_cmd_io);
+
+/* wait for cmd dmas till they are stopped */
+static void hdac_wait_for_cmd_dmas(struct hdac_bus *bus)
+{
+       unsigned long timeout;
+
+       timeout = jiffies + msecs_to_jiffies(100);
+       while ((snd_hdac_chip_readb(bus, RIRBCTL) & AZX_RBCTL_DMA_EN)
+               && time_before(jiffies, timeout))
+               udelay(10);
+
+       timeout = jiffies + msecs_to_jiffies(100);
+       while ((snd_hdac_chip_readb(bus, CORBCTL) & AZX_CORBCTL_RUN)
+               && time_before(jiffies, timeout))
+               udelay(10);
+}
+
+/**
+ * snd_hdac_bus_stop_cmd_io - clean up CORB/RIRB buffers
+ * @bus: HD-audio core bus
+ */
+void snd_hdac_bus_stop_cmd_io(struct hdac_bus *bus)
+{
+       spin_lock_irq(&bus->reg_lock);
+       /* disable ringbuffer DMAs */
+       snd_hdac_chip_writeb(bus, RIRBCTL, 0);
+       snd_hdac_chip_writeb(bus, CORBCTL, 0);
+       spin_unlock_irq(&bus->reg_lock);
+
+       hdac_wait_for_cmd_dmas(bus);
+
+       spin_lock_irq(&bus->reg_lock);
+       /* disable unsolicited responses */
+       snd_hdac_chip_updatel(bus, GCTL, AZX_GCTL_UNSOL, 0);
+       spin_unlock_irq(&bus->reg_lock);
+}
+EXPORT_SYMBOL_GPL(snd_hdac_bus_stop_cmd_io);
+
+static unsigned int azx_command_addr(u32 cmd)
+{
+       unsigned int addr = cmd >> 28;
+
+       if (snd_BUG_ON(addr >= HDA_MAX_CODECS))
+               addr = 0;
+       return addr;
+}
+
+/* receive an Immediate Response with PIO */
+static int snd_hdac_bus_wait_for_pio_response(struct hdac_bus *bus,
+                                             unsigned int addr)
+{
+       int timeout = 50;
+
+       while (timeout--) {
+               /* check IRV bit */
+               if (snd_hdac_chip_readw(bus, IRS) & AZX_IRS_VALID) {
+                       /* reuse rirb.res as the response return value */
+                       bus->rirb.res[addr] = snd_hdac_chip_readl(bus, IR);
+                       return 0;
+               }
+               udelay(1);
+       }
+
+       dev_dbg_ratelimited(bus->dev, "get_response_pio timeout: IRS=%#x\n",
+                           snd_hdac_chip_readw(bus, IRS));
+
+       bus->rirb.res[addr] = -1;
+
+       return -EIO;
+}
+
+/**
+ * snd_hdac_bus_send_cmd_pio - send a command verb via Immediate Command
+ * @bus: HD-audio core bus
+ * @val: encoded verb value to send
+ *
+ * Returns zero for success or a negative error code.
+ */
+static int snd_hdac_bus_send_cmd_pio(struct hdac_bus *bus, unsigned int val)
+{
+       unsigned int addr = azx_command_addr(val);
+       int timeout = 50;
+       int ret = -EIO;
+
+       spin_lock_irq(&bus->reg_lock);
+
+       while (timeout--) {
+               /* check ICB bit */
+               if (!((snd_hdac_chip_readw(bus, IRS) & AZX_IRS_BUSY))) {
+                       /* Clear IRV bit */
+                       snd_hdac_chip_updatew(bus, IRS, AZX_IRS_VALID, AZX_IRS_VALID);
+                       snd_hdac_chip_writel(bus, IC, val);
+                       /* Set ICB bit */
+                       snd_hdac_chip_updatew(bus, IRS, AZX_IRS_BUSY, AZX_IRS_BUSY);
+
+                       ret = snd_hdac_bus_wait_for_pio_response(bus, addr);
+                       goto out;
+               }
+               udelay(1);
+       }
+
+       dev_dbg_ratelimited(bus->dev, "send_cmd_pio timeout: IRS=%#x, val=%#x\n",
+                           snd_hdac_chip_readw(bus, IRS), val);
+
+out:
+       spin_unlock_irq(&bus->reg_lock);
+
+       return ret;
+}
+
+/**
+ * snd_hdac_bus_get_response_pio - receive a response via Immediate Response
+ * @bus: HD-audio core bus
+ * @addr: codec address
+ * @res: pointer to store the value, NULL when not needed
+ *
+ * Returns zero if a value is read, or a negative error code.
+ */
+static int snd_hdac_bus_get_response_pio(struct hdac_bus *bus,
+                                        unsigned int addr, unsigned int *res)
+{
+       if (res)
+               *res = bus->rirb.res[addr];
+
+       return 0;
+}
+
+/**
+ * snd_hdac_bus_send_cmd_corb - send a command verb via CORB
+ * @bus: HD-audio core bus
+ * @val: encoded verb value to send
+ *
+ * Returns zero for success or a negative error code.
+ */
+static int snd_hdac_bus_send_cmd_corb(struct hdac_bus *bus, unsigned int val)
+{
+       unsigned int addr = azx_command_addr(val);
+       unsigned int wp, rp;
+
+       spin_lock_irq(&bus->reg_lock);
+
+       bus->last_cmd[azx_command_addr(val)] = val;
+
+       /* add command to corb */
+       wp = snd_hdac_chip_readw(bus, CORBWP);
+       if (wp == 0xffff) {
+               /* something wrong, controller likely turned to D3 */
+               spin_unlock_irq(&bus->reg_lock);
+               return -EIO;
+       }
+       wp++;
+       wp %= AZX_MAX_CORB_ENTRIES;
+
+       rp = snd_hdac_chip_readw(bus, CORBRP);
+       if (wp == rp) {
+               /* oops, it's full */
+               spin_unlock_irq(&bus->reg_lock);
+               return -EAGAIN;
+       }
+
+       bus->rirb.cmds[addr]++;
+       bus->corb.buf[wp] = cpu_to_le32(val);
+       snd_hdac_chip_writew(bus, CORBWP, wp);
+
+       spin_unlock_irq(&bus->reg_lock);
+
+       return 0;
+}
+
+#define AZX_RIRB_EX_UNSOL_EV   (1<<4)
+
+/**
+ * snd_hdac_bus_update_rirb - retrieve RIRB entries
+ * @bus: HD-audio core bus
+ *
+ * Usually called from interrupt handler.
+ * The caller needs bus->reg_lock spinlock before calling this.
+ */
+void snd_hdac_bus_update_rirb(struct hdac_bus *bus)
+{
+       unsigned int rp, wp;
+       unsigned int addr;
+       u32 res, res_ex;
+
+       wp = snd_hdac_chip_readw(bus, RIRBWP);
+       if (wp == 0xffff) {
+               /* something wrong, controller likely turned to D3 */
+               return;
+       }
+
+       if (wp == bus->rirb.wp)
+               return;
+       bus->rirb.wp = wp;
+
+       while (bus->rirb.rp != wp) {
+               bus->rirb.rp++;
+               bus->rirb.rp %= AZX_MAX_RIRB_ENTRIES;
+
+               rp = bus->rirb.rp << 1; /* an RIRB entry is 8-bytes */
+               res_ex = le32_to_cpu(bus->rirb.buf[rp + 1]);
+               res = le32_to_cpu(bus->rirb.buf[rp]);
+               addr = res_ex & 0xf;
+               if (addr >= HDA_MAX_CODECS) {
+                       dev_err(bus->dev,
+                               "spurious response %#x:%#x, rp = %d, wp = %d",
+                               res, res_ex, bus->rirb.rp, wp);
+                       snd_BUG();
+               } else if (res_ex & AZX_RIRB_EX_UNSOL_EV)
+                       snd_hdac_bus_queue_event(bus, res, res_ex);
+               else if (bus->rirb.cmds[addr]) {
+                       bus->rirb.res[addr] = res;
+                       bus->rirb.cmds[addr]--;
+                       if (!bus->rirb.cmds[addr] &&
+                           waitqueue_active(&bus->rirb_wq))
+                               wake_up(&bus->rirb_wq);
+               } else {
+                       dev_err_ratelimited(bus->dev,
+                               "spurious response %#x:%#x, last cmd=%#08x\n",
+                               res, res_ex, bus->last_cmd[addr]);
+               }
+       }
+}
+EXPORT_SYMBOL_GPL(snd_hdac_bus_update_rirb);
+
+/**
+ * snd_hdac_bus_get_response_rirb - receive a response via RIRB
+ * @bus: HD-audio core bus
+ * @addr: codec address
+ * @res: pointer to store the value, NULL when not needed
+ *
+ * Returns zero if a value is read, or a negative error code.
+ */
+static int snd_hdac_bus_get_response_rirb(struct hdac_bus *bus,
+                                         unsigned int addr, unsigned int *res)
+{
+       unsigned long timeout;
+       unsigned long loopcounter;
+       wait_queue_entry_t wait;
+       bool warned = false;
+
+       init_wait_entry(&wait, 0);
+       timeout = jiffies + msecs_to_jiffies(1000);
+
+       for (loopcounter = 0;; loopcounter++) {
+               spin_lock_irq(&bus->reg_lock);
+               if (!bus->polling_mode)
+                       prepare_to_wait(&bus->rirb_wq, &wait,
+                                       TASK_UNINTERRUPTIBLE);
+               if (bus->polling_mode)
+                       snd_hdac_bus_update_rirb(bus);
+               if (!bus->rirb.cmds[addr]) {
+                       if (res)
+                               *res = bus->rirb.res[addr]; /* the last value */
+                       if (!bus->polling_mode)
+                               finish_wait(&bus->rirb_wq, &wait);
+                       spin_unlock_irq(&bus->reg_lock);
+                       return 0;
+               }
+               spin_unlock_irq(&bus->reg_lock);
+               if (time_after(jiffies, timeout))
+                       break;
+#define LOOP_COUNT_MAX 3000
+               if (!bus->polling_mode) {
+                       schedule_timeout(msecs_to_jiffies(2));
+               } else if (bus->needs_damn_long_delay ||
+                          loopcounter > LOOP_COUNT_MAX) {
+                       if (loopcounter > LOOP_COUNT_MAX && !warned) {
+                               dev_dbg_ratelimited(bus->dev,
+                                                   "too slow response, last cmd=%#08x\n",
+                                                   bus->last_cmd[addr]);
+                               warned = true;
+                       }
+                       msleep(2); /* temporary workaround */
+               } else {
+                       udelay(10);
+                       cond_resched();
+               }
+       }
+
+       if (!bus->polling_mode)
+               finish_wait(&bus->rirb_wq, &wait);
+
+       return -EIO;
+}
+
+/**
+ * snd_hdac_bus_send_cmd - send a command verb via CORB or PIO
+ * @bus: HD-audio core bus
+ * @val: encoded verb value to send
+ *
+ * Returns zero for success or a negative error code.
+ */
+int snd_hdac_bus_send_cmd(struct hdac_bus *bus, unsigned int val)
+{
+       if (bus->use_pio_for_commands)
+               return snd_hdac_bus_send_cmd_pio(bus, val);
+
+       return snd_hdac_bus_send_cmd_corb(bus, val);
+}
+EXPORT_SYMBOL_GPL(snd_hdac_bus_send_cmd);
+
+/**
+ * snd_hdac_bus_get_response - receive a response via RIRB or PIO
+ * @bus: HD-audio core bus
+ * @addr: codec address
+ * @res: pointer to store the value, NULL when not needed
+ *
+ * Returns zero if a value is read, or a negative error code.
+ */
+int snd_hdac_bus_get_response(struct hdac_bus *bus, unsigned int addr,
+                             unsigned int *res)
+{
+       if (bus->use_pio_for_commands)
+               return snd_hdac_bus_get_response_pio(bus, addr, res);
+
+       return snd_hdac_bus_get_response_rirb(bus, addr, res);
+}
+EXPORT_SYMBOL_GPL(snd_hdac_bus_get_response);
+
+#define HDAC_MAX_CAPS 10
+/**
+ * snd_hdac_bus_parse_capabilities - parse capability structure
+ * @bus: the pointer to bus object
+ *
+ * Returns 0 if successful, or a negative error code.
+ */
+int snd_hdac_bus_parse_capabilities(struct hdac_bus *bus)
+{
+       unsigned int cur_cap;
+       unsigned int offset;
+       unsigned int counter = 0;
+
+       offset = snd_hdac_chip_readw(bus, LLCH);
+
+       /* Lets walk the linked capabilities list */
+       do {
+               cur_cap = _snd_hdac_chip_readl(bus, offset);
+
+               dev_dbg(bus->dev, "Capability version: 0x%x\n",
+                       (cur_cap & AZX_CAP_HDR_VER_MASK) >> AZX_CAP_HDR_VER_OFF);
+
+               dev_dbg(bus->dev, "HDA capability ID: 0x%x\n",
+                       (cur_cap & AZX_CAP_HDR_ID_MASK) >> AZX_CAP_HDR_ID_OFF);
+
+               if (cur_cap == -1) {
+                       dev_dbg(bus->dev, "Invalid capability reg read\n");
+                       break;
+               }
+
+               switch ((cur_cap & AZX_CAP_HDR_ID_MASK) >> AZX_CAP_HDR_ID_OFF) {
+               case AZX_ML_CAP_ID:
+                       dev_dbg(bus->dev, "Found ML capability\n");
+                       bus->mlcap = bus->remap_addr + offset;
+                       break;
+
+               case AZX_GTS_CAP_ID:
+                       dev_dbg(bus->dev, "Found GTS capability offset=%x\n", offset);
+                       bus->gtscap = bus->remap_addr + offset;
+                       break;
+
+               case AZX_PP_CAP_ID:
+                       /* PP capability found, the Audio DSP is present */
+                       dev_dbg(bus->dev, "Found PP capability offset=%x\n", offset);
+                       bus->ppcap = bus->remap_addr + offset;
+                       break;
+
+               case AZX_SPB_CAP_ID:
+                       /* SPIB capability found, handler function */
+                       dev_dbg(bus->dev, "Found SPB capability\n");
+                       bus->spbcap = bus->remap_addr + offset;
+                       break;
+
+               case AZX_DRSM_CAP_ID:
+                       /* DMA resume  capability found, handler function */
+                       dev_dbg(bus->dev, "Found DRSM capability\n");
+                       bus->drsmcap = bus->remap_addr + offset;
+                       break;
+
+               default:
+                       dev_err(bus->dev, "Unknown capability %d\n", cur_cap);
+                       cur_cap = 0;
+                       break;
+               }
+
+               counter++;
+
+               if (counter > HDAC_MAX_CAPS) {
+                       dev_err(bus->dev, "We exceeded HDAC capabilities!!!\n");
+                       break;
+               }
+
+               /* read the offset of next capability */
+               offset = cur_cap & AZX_CAP_HDR_NXT_PTR_MASK;
+
+       } while (offset);
+
+       return 0;
+}
+EXPORT_SYMBOL_GPL(snd_hdac_bus_parse_capabilities);
+
+/*
+ * Lowlevel interface
+ */
+
+/**
+ * snd_hdac_bus_enter_link_reset - enter link reset
+ * @bus: HD-audio core bus
+ *
+ * Enter to the link reset state.
+ */
+void snd_hdac_bus_enter_link_reset(struct hdac_bus *bus)
+{
+       unsigned long timeout;
+
+       /* reset controller */
+       snd_hdac_chip_updatel(bus, GCTL, AZX_GCTL_RESET, 0);
+
+       timeout = jiffies + msecs_to_jiffies(100);
+       while ((snd_hdac_chip_readb(bus, GCTL) & AZX_GCTL_RESET) &&
+              time_before(jiffies, timeout))
+               usleep_range(500, 1000);
+}
+EXPORT_SYMBOL_GPL(snd_hdac_bus_enter_link_reset);
+
+/**
+ * snd_hdac_bus_exit_link_reset - exit link reset
+ * @bus: HD-audio core bus
+ *
+ * Exit from the link reset state.
+ */
+void snd_hdac_bus_exit_link_reset(struct hdac_bus *bus)
+{
+       unsigned long timeout;
+
+       snd_hdac_chip_updateb(bus, GCTL, AZX_GCTL_RESET, AZX_GCTL_RESET);
+
+       timeout = jiffies + msecs_to_jiffies(100);
+       while (!snd_hdac_chip_readb(bus, GCTL) && time_before(jiffies, timeout))
+               usleep_range(500, 1000);
+}
+EXPORT_SYMBOL_GPL(snd_hdac_bus_exit_link_reset);
+
+/* reset codec link */
+int snd_hdac_bus_reset_link(struct hdac_bus *bus, bool full_reset)
+{
+       if (!full_reset)
+               goto skip_reset;
+
+       /* clear STATESTS if not in reset */
+       if (snd_hdac_chip_readb(bus, GCTL) & AZX_GCTL_RESET)
+               snd_hdac_chip_writew(bus, STATESTS, STATESTS_INT_MASK);
+
+       /* reset controller */
+       snd_hdac_bus_enter_link_reset(bus);
+
+       /* delay for >= 100us for codec PLL to settle per spec
+        * Rev 0.9 section 5.5.1
+        */
+       usleep_range(500, 1000);
+
+       /* Bring controller out of reset */
+       snd_hdac_bus_exit_link_reset(bus);
+
+       /* Brent Chartrand said to wait >= 540us for codecs to initialize */
+       usleep_range(1000, 1200);
+
+ skip_reset:
+       /* check to see if controller is ready */
+       if (!snd_hdac_chip_readb(bus, GCTL)) {
+               dev_dbg(bus->dev, "controller not ready!\n");
+               return -EBUSY;
+       }
+
+       /* detect codecs */
+       if (!bus->codec_mask) {
+               bus->codec_mask = snd_hdac_chip_readw(bus, STATESTS);
+               dev_dbg(bus->dev, "codec_mask = 0x%lx\n", bus->codec_mask);
+       }
+
+       return 0;
+}
+EXPORT_SYMBOL_GPL(snd_hdac_bus_reset_link);
+
+/* enable interrupts */
+static void azx_int_enable(struct hdac_bus *bus)
+{
+       /* enable controller CIE and GIE */
+       snd_hdac_chip_updatel(bus, INTCTL,
+                             AZX_INT_CTRL_EN | AZX_INT_GLOBAL_EN,
+                             AZX_INT_CTRL_EN | AZX_INT_GLOBAL_EN);
+}
+
+/* disable interrupts */
+static void azx_int_disable(struct hdac_bus *bus)
+{
+       struct hdac_stream *azx_dev;
+
+       /* disable interrupts in stream descriptor */
+       list_for_each_entry(azx_dev, &bus->stream_list, list)
+               snd_hdac_stream_updateb(azx_dev, SD_CTL, SD_INT_MASK, 0);
+
+       /* disable SIE for all streams & disable controller CIE and GIE */
+       snd_hdac_chip_writel(bus, INTCTL, 0);
+}
+
+/* clear interrupts */
+static void azx_int_clear(struct hdac_bus *bus)
+{
+       struct hdac_stream *azx_dev;
+
+       /* clear stream status */
+       list_for_each_entry(azx_dev, &bus->stream_list, list)
+               snd_hdac_stream_writeb(azx_dev, SD_STS, SD_INT_MASK);
+
+       /* clear STATESTS */
+       snd_hdac_chip_writew(bus, STATESTS, STATESTS_INT_MASK);
+
+       /* clear rirb status */
+       snd_hdac_chip_writeb(bus, RIRBSTS, RIRB_INT_MASK);
+
+       /* clear int status */
+       snd_hdac_chip_writel(bus, INTSTS, AZX_INT_CTRL_EN | AZX_INT_ALL_STREAM);
+}
+
+/**
+ * snd_hdac_bus_init_chip - reset and start the controller registers
+ * @bus: HD-audio core bus
+ * @full_reset: Do full reset
+ */
+bool snd_hdac_bus_init_chip(struct hdac_bus *bus, bool full_reset)
+{
+       if (bus->chip_init)
+               return false;
+
+       /* reset controller */
+       snd_hdac_bus_reset_link(bus, full_reset);
+
+       /* clear interrupts */
+       azx_int_clear(bus);
+
+       /* initialize the codec command I/O */
+       snd_hdac_bus_init_cmd_io(bus);
+
+       /* enable interrupts after CORB/RIRB buffers are initialized above */
+       azx_int_enable(bus);
+
+       /* program the position buffer */
+       if (bus->use_posbuf && bus->posbuf.addr) {
+               snd_hdac_chip_writel(bus, DPLBASE, (u32)bus->posbuf.addr);
+               snd_hdac_chip_writel(bus, DPUBASE, upper_32_bits(bus->posbuf.addr));
+       }
+
+       bus->chip_init = true;
+
+       return true;
+}
+EXPORT_SYMBOL_GPL(snd_hdac_bus_init_chip);
+
+/**
+ * snd_hdac_bus_stop_chip - disable the whole IRQ and I/Os
+ * @bus: HD-audio core bus
+ */
+void snd_hdac_bus_stop_chip(struct hdac_bus *bus)
+{
+       if (!bus->chip_init)
+               return;
+
+       /* disable interrupts */
+       azx_int_disable(bus);
+       azx_int_clear(bus);
+
+       /* disable CORB/RIRB */
+       snd_hdac_bus_stop_cmd_io(bus);
+
+       /* disable position buffer */
+       if (bus->posbuf.addr) {
+               snd_hdac_chip_writel(bus, DPLBASE, 0);
+               snd_hdac_chip_writel(bus, DPUBASE, 0);
+       }
+
+       bus->chip_init = false;
+}
+EXPORT_SYMBOL_GPL(snd_hdac_bus_stop_chip);
+
+/**
+ * snd_hdac_bus_handle_stream_irq - interrupt handler for streams
+ * @bus: HD-audio core bus
+ * @status: INTSTS register value
+ * @ack: callback to be called for woken streams
+ *
+ * Returns the bits of handled streams, or zero if no stream is handled.
+ */
+int snd_hdac_bus_handle_stream_irq(struct hdac_bus *bus, unsigned int status,
+                                   void (*ack)(struct hdac_bus *,
+                                               struct hdac_stream *))
+{
+       struct hdac_stream *azx_dev;
+       u8 sd_status;
+       int handled = 0;
+
+       list_for_each_entry(azx_dev, &bus->stream_list, list) {
+               if (status & azx_dev->sd_int_sta_mask) {
+                       sd_status = snd_hdac_stream_readb(azx_dev, SD_STS);
+                       snd_hdac_stream_writeb(azx_dev, SD_STS, SD_INT_MASK);
+                       handled |= 1 << azx_dev->index;
+                       if ((!azx_dev->substream && !azx_dev->cstream) ||
+                           !azx_dev->running || !(sd_status & SD_INT_COMPLETE))
+                               continue;
+                       if (ack)
+                               ack(bus, azx_dev);
+               }
+       }
+       return handled;
+}
+EXPORT_SYMBOL_GPL(snd_hdac_bus_handle_stream_irq);
+
+/**
+ * snd_hdac_bus_alloc_stream_pages - allocate BDL and other buffers
+ * @bus: HD-audio core bus
+ *
+ * Call this after assigning the all streams.
+ * Returns zero for success, or a negative error code.
+ */
+int snd_hdac_bus_alloc_stream_pages(struct hdac_bus *bus)
+{
+       struct hdac_stream *s;
+       int num_streams = 0;
+       int dma_type = bus->dma_type ? bus->dma_type : SNDRV_DMA_TYPE_DEV;
+       int err;
+
+       list_for_each_entry(s, &bus->stream_list, list) {
+               /* allocate memory for the BDL for each stream */
+               err = snd_dma_alloc_pages(dma_type, bus->dev,
+                                         BDL_SIZE, &s->bdl);
+               num_streams++;
+               if (err < 0)
+                       return -ENOMEM;
+       }
+
+       if (WARN_ON(!num_streams))
+               return -EINVAL;
+       /* allocate memory for the position buffer */
+       err = snd_dma_alloc_pages(dma_type, bus->dev,
+                                 num_streams * 8, &bus->posbuf);
+       if (err < 0)
+               return -ENOMEM;
+       list_for_each_entry(s, &bus->stream_list, list)
+               s->posbuf = (__le32 *)(bus->posbuf.area + s->index * 8);
+
+       /* single page (at least 4096 bytes) must suffice for both ringbuffes */
+       return snd_dma_alloc_pages(dma_type, bus->dev, PAGE_SIZE, &bus->rb);
+}
+EXPORT_SYMBOL_GPL(snd_hdac_bus_alloc_stream_pages);
+
+/**
+ * snd_hdac_bus_free_stream_pages - release BDL and other buffers
+ * @bus: HD-audio core bus
+ */
+void snd_hdac_bus_free_stream_pages(struct hdac_bus *bus)
+{
+       struct hdac_stream *s;
+
+       list_for_each_entry(s, &bus->stream_list, list) {
+               if (s->bdl.area)
+                       snd_dma_free_pages(&s->bdl);
+       }
+
+       if (bus->rb.area)
+               snd_dma_free_pages(&bus->rb);
+       if (bus->posbuf.area)
+               snd_dma_free_pages(&bus->posbuf);
+}
+EXPORT_SYMBOL_GPL(snd_hdac_bus_free_stream_pages);
+
+/**
+ * snd_hdac_bus_link_power - power up/down codec link
+ * @codec: HD-audio device
+ * @enable: whether to power-up the link
+ */
+void snd_hdac_bus_link_power(struct hdac_device *codec, bool enable)
+{
+       if (enable)
+               set_bit(codec->addr, &codec->bus->codec_powered);
+       else
+               clear_bit(codec->addr, &codec->bus->codec_powered);
+}
+EXPORT_SYMBOL_GPL(snd_hdac_bus_link_power);
diff --git a/sound/hda/core/device.c b/sound/hda/core/device.c
new file mode 100644 (file)
index 0000000..018f9e1
--- /dev/null
@@ -0,0 +1,1170 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * HD-audio codec core device
+ */
+
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/export.h>
+#include <linux/pm_runtime.h>
+#include <sound/hdaudio.h>
+#include <sound/hda_regmap.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include "local.h"
+
+static void setup_fg_nodes(struct hdac_device *codec);
+static int get_codec_vendor_name(struct hdac_device *codec);
+
+static void default_release(struct device *dev)
+{
+       snd_hdac_device_exit(dev_to_hdac_dev(dev));
+}
+
+/**
+ * snd_hdac_device_init - initialize the HD-audio codec base device
+ * @codec: device to initialize
+ * @bus: but to attach
+ * @name: device name string
+ * @addr: codec address
+ *
+ * Returns zero for success or a negative error code.
+ *
+ * This function increments the runtime PM counter and marks it active.
+ * The caller needs to turn it off appropriately later.
+ *
+ * The caller needs to set the device's release op properly by itself.
+ */
+int snd_hdac_device_init(struct hdac_device *codec, struct hdac_bus *bus,
+                        const char *name, unsigned int addr)
+{
+       struct device *dev;
+       hda_nid_t fg;
+       int err;
+
+       dev = &codec->dev;
+       device_initialize(dev);
+       dev->parent = bus->dev;
+       dev->bus = &snd_hda_bus_type;
+       dev->release = default_release;
+       dev->groups = hdac_dev_attr_groups;
+       dev_set_name(dev, "%s", name);
+       device_enable_async_suspend(dev);
+
+       codec->bus = bus;
+       codec->addr = addr;
+       codec->type = HDA_DEV_CORE;
+       mutex_init(&codec->widget_lock);
+       mutex_init(&codec->regmap_lock);
+       pm_runtime_set_active(&codec->dev);
+       pm_runtime_get_noresume(&codec->dev);
+       atomic_set(&codec->in_pm, 0);
+
+       err = snd_hdac_bus_add_device(bus, codec);
+       if (err < 0)
+               goto error;
+
+       /* fill parameters */
+       codec->vendor_id = snd_hdac_read_parm(codec, AC_NODE_ROOT,
+                                             AC_PAR_VENDOR_ID);
+       if (codec->vendor_id == -1) {
+               /* read again, hopefully the access method was corrected
+                * in the last read...
+                */
+               codec->vendor_id = snd_hdac_read_parm(codec, AC_NODE_ROOT,
+                                                     AC_PAR_VENDOR_ID);
+       }
+
+       codec->subsystem_id = snd_hdac_read_parm(codec, AC_NODE_ROOT,
+                                                AC_PAR_SUBSYSTEM_ID);
+       codec->revision_id = snd_hdac_read_parm(codec, AC_NODE_ROOT,
+                                               AC_PAR_REV_ID);
+
+       setup_fg_nodes(codec);
+       if (!codec->afg && !codec->mfg) {
+               dev_err(dev, "no AFG or MFG node found\n");
+               err = -ENODEV;
+               goto error;
+       }
+
+       fg = codec->afg ? codec->afg : codec->mfg;
+
+       err = snd_hdac_refresh_widgets(codec);
+       if (err < 0)
+               goto error;
+
+       codec->power_caps = snd_hdac_read_parm(codec, fg, AC_PAR_POWER_STATE);
+       /* reread ssid if not set by parameter */
+       if (codec->subsystem_id == -1 || codec->subsystem_id == 0)
+               snd_hdac_read(codec, fg, AC_VERB_GET_SUBSYSTEM_ID, 0,
+                             &codec->subsystem_id);
+
+       err = get_codec_vendor_name(codec);
+       if (err < 0)
+               goto error;
+
+       codec->chip_name = kasprintf(GFP_KERNEL, "ID %x",
+                                    codec->vendor_id & 0xffff);
+       if (!codec->chip_name) {
+               err = -ENOMEM;
+               goto error;
+       }
+
+       return 0;
+
+ error:
+       put_device(&codec->dev);
+       return err;
+}
+EXPORT_SYMBOL_GPL(snd_hdac_device_init);
+
+/**
+ * snd_hdac_device_exit - clean up the HD-audio codec base device
+ * @codec: device to clean up
+ */
+void snd_hdac_device_exit(struct hdac_device *codec)
+{
+       pm_runtime_put_noidle(&codec->dev);
+       /* keep balance of runtime PM child_count in parent device */
+       pm_runtime_set_suspended(&codec->dev);
+       snd_hdac_bus_remove_device(codec->bus, codec);
+       kfree(codec->vendor_name);
+       kfree(codec->chip_name);
+}
+EXPORT_SYMBOL_GPL(snd_hdac_device_exit);
+
+/**
+ * snd_hdac_device_register - register the hd-audio codec base device
+ * @codec: the device to register
+ */
+int snd_hdac_device_register(struct hdac_device *codec)
+{
+       int err;
+
+       err = device_add(&codec->dev);
+       if (err < 0)
+               return err;
+       mutex_lock(&codec->widget_lock);
+       err = hda_widget_sysfs_init(codec);
+       mutex_unlock(&codec->widget_lock);
+       if (err < 0) {
+               device_del(&codec->dev);
+               return err;
+       }
+
+       return 0;
+}
+EXPORT_SYMBOL_GPL(snd_hdac_device_register);
+
+/**
+ * snd_hdac_device_unregister - unregister the hd-audio codec base device
+ * @codec: the device to unregister
+ */
+void snd_hdac_device_unregister(struct hdac_device *codec)
+{
+       if (device_is_registered(&codec->dev)) {
+               mutex_lock(&codec->widget_lock);
+               hda_widget_sysfs_exit(codec);
+               mutex_unlock(&codec->widget_lock);
+               device_del(&codec->dev);
+               snd_hdac_bus_remove_device(codec->bus, codec);
+       }
+}
+EXPORT_SYMBOL_GPL(snd_hdac_device_unregister);
+
+/**
+ * snd_hdac_device_set_chip_name - set/update the codec name
+ * @codec: the HDAC device
+ * @name: name string to set
+ *
+ * Returns 0 if the name is set or updated, or a negative error code.
+ */
+int snd_hdac_device_set_chip_name(struct hdac_device *codec, const char *name)
+{
+       char *newname;
+
+       if (!name)
+               return 0;
+       newname = kstrdup(name, GFP_KERNEL);
+       if (!newname)
+               return -ENOMEM;
+       kfree(codec->chip_name);
+       codec->chip_name = newname;
+       return 0;
+}
+EXPORT_SYMBOL_GPL(snd_hdac_device_set_chip_name);
+
+/**
+ * snd_hdac_codec_modalias - give the module alias name
+ * @codec: HDAC device
+ * @buf: string buffer to store
+ * @size: string buffer size
+ *
+ * Returns the size of string, like snprintf(), or a negative error code.
+ */
+int snd_hdac_codec_modalias(const struct hdac_device *codec, char *buf, size_t size)
+{
+       return scnprintf(buf, size, "hdaudio:v%08Xr%08Xa%02X\n",
+                       codec->vendor_id, codec->revision_id, codec->type);
+}
+EXPORT_SYMBOL_GPL(snd_hdac_codec_modalias);
+
+/**
+ * snd_hdac_make_cmd - compose a 32bit command word to be sent to the
+ *     HD-audio controller
+ * @codec: the codec object
+ * @nid: NID to encode
+ * @verb: verb to encode
+ * @parm: parameter to encode
+ *
+ * Return an encoded command verb or -1 for error.
+ */
+static unsigned int snd_hdac_make_cmd(struct hdac_device *codec, hda_nid_t nid,
+                                     unsigned int verb, unsigned int parm)
+{
+       u32 val, addr;
+
+       addr = codec->addr;
+       if ((addr & ~0xf) || (nid & ~0x7f) ||
+           (verb & ~0xfff) || (parm & ~0xffff)) {
+               dev_err(&codec->dev, "out of range cmd %x:%x:%x:%x\n",
+                       addr, nid, verb, parm);
+               return -1;
+       }
+
+       val = addr << 28;
+       val |= (u32)nid << 20;
+       val |= verb << 8;
+       val |= parm;
+       return val;
+}
+
+/**
+ * snd_hdac_exec_verb - execute an encoded verb
+ * @codec: the codec object
+ * @cmd: encoded verb to execute
+ * @flags: optional flags, pass zero for default
+ * @res: the pointer to store the result, NULL if running async
+ *
+ * Returns zero if successful, or a negative error code.
+ *
+ * This calls the exec_verb op when set in hdac_codec.  If not,
+ * call the default snd_hdac_bus_exec_verb().
+ */
+int snd_hdac_exec_verb(struct hdac_device *codec, unsigned int cmd,
+                      unsigned int flags, unsigned int *res)
+{
+       if (codec->exec_verb)
+               return codec->exec_verb(codec, cmd, flags, res);
+       return snd_hdac_bus_exec_verb(codec->bus, codec->addr, cmd, res);
+}
+
+
+/**
+ * snd_hdac_read - execute a verb
+ * @codec: the codec object
+ * @nid: NID to execute a verb
+ * @verb: verb to execute
+ * @parm: parameter for a verb
+ * @res: the pointer to store the result, NULL if running async
+ *
+ * Returns zero if successful, or a negative error code.
+ */
+int snd_hdac_read(struct hdac_device *codec, hda_nid_t nid,
+                 unsigned int verb, unsigned int parm, unsigned int *res)
+{
+       unsigned int cmd = snd_hdac_make_cmd(codec, nid, verb, parm);
+
+       return snd_hdac_exec_verb(codec, cmd, 0, res);
+}
+EXPORT_SYMBOL_GPL(snd_hdac_read);
+
+/**
+ * _snd_hdac_read_parm - read a parmeter
+ * @codec: the codec object
+ * @nid: NID to read a parameter
+ * @parm: parameter to read
+ * @res: pointer to store the read value
+ *
+ * This function returns zero or an error unlike snd_hdac_read_parm().
+ */
+int _snd_hdac_read_parm(struct hdac_device *codec, hda_nid_t nid, int parm,
+                       unsigned int *res)
+{
+       unsigned int cmd;
+
+       cmd = snd_hdac_regmap_encode_verb(nid, AC_VERB_PARAMETERS) | parm;
+       return snd_hdac_regmap_read_raw(codec, cmd, res);
+}
+EXPORT_SYMBOL_GPL(_snd_hdac_read_parm);
+
+/**
+ * snd_hdac_read_parm_uncached - read a codec parameter without caching
+ * @codec: the codec object
+ * @nid: NID to read a parameter
+ * @parm: parameter to read
+ *
+ * Returns -1 for error.  If you need to distinguish the error more
+ * strictly, use snd_hdac_read() directly.
+ */
+int snd_hdac_read_parm_uncached(struct hdac_device *codec, hda_nid_t nid,
+                               int parm)
+{
+       unsigned int cmd, val;
+
+       cmd = snd_hdac_regmap_encode_verb(nid, AC_VERB_PARAMETERS) | parm;
+       if (snd_hdac_regmap_read_raw_uncached(codec, cmd, &val) < 0)
+               return -1;
+       return val;
+}
+EXPORT_SYMBOL_GPL(snd_hdac_read_parm_uncached);
+
+/**
+ * snd_hdac_override_parm - override read-only parameters
+ * @codec: the codec object
+ * @nid: NID for the parameter
+ * @parm: the parameter to change
+ * @val: the parameter value to overwrite
+ */
+int snd_hdac_override_parm(struct hdac_device *codec, hda_nid_t nid,
+                          unsigned int parm, unsigned int val)
+{
+       unsigned int verb = (AC_VERB_PARAMETERS << 8) | (nid << 20) | parm;
+       int err;
+
+       if (!codec->regmap)
+               return -EINVAL;
+
+       codec->caps_overwriting = true;
+       err = snd_hdac_regmap_write_raw(codec, verb, val);
+       codec->caps_overwriting = false;
+       return err;
+}
+EXPORT_SYMBOL_GPL(snd_hdac_override_parm);
+
+/**
+ * snd_hdac_get_sub_nodes - get start NID and number of subtree nodes
+ * @codec: the codec object
+ * @nid: NID to inspect
+ * @start_id: the pointer to store the starting NID
+ *
+ * Returns the number of subtree nodes or zero if not found.
+ * This function reads parameters always without caching.
+ */
+int snd_hdac_get_sub_nodes(struct hdac_device *codec, hda_nid_t nid,
+                          hda_nid_t *start_id)
+{
+       unsigned int parm;
+
+       parm = snd_hdac_read_parm_uncached(codec, nid, AC_PAR_NODE_COUNT);
+       if (parm == -1) {
+               *start_id = 0;
+               return 0;
+       }
+       *start_id = (parm >> 16) & 0x7fff;
+       return (int)(parm & 0x7fff);
+}
+EXPORT_SYMBOL_GPL(snd_hdac_get_sub_nodes);
+
+/*
+ * look for an AFG and MFG nodes
+ */
+static void setup_fg_nodes(struct hdac_device *codec)
+{
+       int i, total_nodes, function_id;
+       hda_nid_t nid;
+
+       total_nodes = snd_hdac_get_sub_nodes(codec, AC_NODE_ROOT, &nid);
+       for (i = 0; i < total_nodes; i++, nid++) {
+               function_id = snd_hdac_read_parm(codec, nid,
+                                                AC_PAR_FUNCTION_TYPE);
+               switch (function_id & 0xff) {
+               case AC_GRP_AUDIO_FUNCTION:
+                       codec->afg = nid;
+                       codec->afg_function_id = function_id & 0xff;
+                       codec->afg_unsol = (function_id >> 8) & 1;
+                       break;
+               case AC_GRP_MODEM_FUNCTION:
+                       codec->mfg = nid;
+                       codec->mfg_function_id = function_id & 0xff;
+                       codec->mfg_unsol = (function_id >> 8) & 1;
+                       break;
+               default:
+                       break;
+               }
+       }
+}
+
+/**
+ * snd_hdac_refresh_widgets - Reset the widget start/end nodes
+ * @codec: the codec object
+ */
+int snd_hdac_refresh_widgets(struct hdac_device *codec)
+{
+       hda_nid_t start_nid;
+       int nums, err = 0;
+
+       /*
+        * Serialize against multiple threads trying to update the sysfs
+        * widgets array.
+        */
+       mutex_lock(&codec->widget_lock);
+       nums = snd_hdac_get_sub_nodes(codec, codec->afg, &start_nid);
+       if (!start_nid || nums <= 0 || nums >= 0xff) {
+               dev_err(&codec->dev, "cannot read sub nodes for FG 0x%02x\n",
+                       codec->afg);
+               err = -EINVAL;
+               goto unlock;
+       }
+
+       err = hda_widget_sysfs_reinit(codec, start_nid, nums);
+       if (err < 0)
+               goto unlock;
+
+       codec->num_nodes = nums;
+       codec->start_nid = start_nid;
+       codec->end_nid = start_nid + nums;
+unlock:
+       mutex_unlock(&codec->widget_lock);
+       return err;
+}
+EXPORT_SYMBOL_GPL(snd_hdac_refresh_widgets);
+
+/* return CONNLIST_LEN parameter of the given widget */
+static unsigned int get_num_conns(struct hdac_device *codec, hda_nid_t nid)
+{
+       unsigned int wcaps = snd_hdac_get_wcaps(codec, nid);
+       unsigned int parm;
+
+       if (!(wcaps & AC_WCAP_CONN_LIST) &&
+           snd_hdac_get_wcaps_type(wcaps) != AC_WID_VOL_KNB)
+               return 0;
+
+       parm = snd_hdac_read_parm(codec, nid, AC_PAR_CONNLIST_LEN);
+       if (parm == -1)
+               parm = 0;
+       return parm;
+}
+
+/**
+ * snd_hdac_get_connections - get a widget connection list
+ * @codec: the codec object
+ * @nid: NID
+ * @conn_list: the array to store the results, can be NULL
+ * @max_conns: the max size of the given array
+ *
+ * Returns the number of connected widgets, zero for no connection, or a
+ * negative error code.  When the number of elements don't fit with the
+ * given array size, it returns -ENOSPC.
+ *
+ * When @conn_list is NULL, it just checks the number of connections.
+ */
+int snd_hdac_get_connections(struct hdac_device *codec, hda_nid_t nid,
+                            hda_nid_t *conn_list, int max_conns)
+{
+       unsigned int parm;
+       int i, conn_len, conns, err;
+       unsigned int shift, num_elems, mask;
+       hda_nid_t prev_nid;
+       int null_count = 0;
+
+       parm = get_num_conns(codec, nid);
+       if (!parm)
+               return 0;
+
+       if (parm & AC_CLIST_LONG) {
+               /* long form */
+               shift = 16;
+               num_elems = 2;
+       } else {
+               /* short form */
+               shift = 8;
+               num_elems = 4;
+       }
+       conn_len = parm & AC_CLIST_LENGTH;
+       mask = (1 << (shift-1)) - 1;
+
+       if (!conn_len)
+               return 0; /* no connection */
+
+       if (conn_len == 1) {
+               /* single connection */
+               err = snd_hdac_read(codec, nid, AC_VERB_GET_CONNECT_LIST, 0,
+                                   &parm);
+               if (err < 0)
+                       return err;
+               if (conn_list)
+                       conn_list[0] = parm & mask;
+               return 1;
+       }
+
+       /* multi connection */
+       conns = 0;
+       prev_nid = 0;
+       for (i = 0; i < conn_len; i++) {
+               int range_val;
+               hda_nid_t val, n;
+
+               if (i % num_elems == 0) {
+                       err = snd_hdac_read(codec, nid,
+                                           AC_VERB_GET_CONNECT_LIST, i,
+                                           &parm);
+                       if (err < 0)
+                               return -EIO;
+               }
+               range_val = !!(parm & (1 << (shift-1))); /* ranges */
+               val = parm & mask;
+               if (val == 0 && null_count++) {  /* no second chance */
+                       dev_dbg(&codec->dev,
+                               "invalid CONNECT_LIST verb %x[%i]:%x\n",
+                               nid, i, parm);
+                       return 0;
+               }
+               parm >>= shift;
+               if (range_val) {
+                       /* ranges between the previous and this one */
+                       if (!prev_nid || prev_nid >= val) {
+                               dev_warn(&codec->dev,
+                                        "invalid dep_range_val %x:%x\n",
+                                        prev_nid, val);
+                               continue;
+                       }
+                       for (n = prev_nid + 1; n <= val; n++) {
+                               if (conn_list) {
+                                       if (conns >= max_conns)
+                                               return -ENOSPC;
+                                       conn_list[conns] = n;
+                               }
+                               conns++;
+                       }
+               } else {
+                       if (conn_list) {
+                               if (conns >= max_conns)
+                                       return -ENOSPC;
+                               conn_list[conns] = val;
+                       }
+                       conns++;
+               }
+               prev_nid = val;
+       }
+       return conns;
+}
+EXPORT_SYMBOL_GPL(snd_hdac_get_connections);
+
+#ifdef CONFIG_PM
+/**
+ * snd_hdac_power_up - power up the codec
+ * @codec: the codec object
+ *
+ * This function calls the runtime PM helper to power up the given codec.
+ * Unlike snd_hdac_power_up_pm(), you should call this only for the code
+ * path that isn't included in PM path.  Otherwise it gets stuck.
+ *
+ * Returns zero if successful, or a negative error code.
+ */
+int snd_hdac_power_up(struct hdac_device *codec)
+{
+       return pm_runtime_get_sync(&codec->dev);
+}
+EXPORT_SYMBOL_GPL(snd_hdac_power_up);
+
+/**
+ * snd_hdac_power_down - power down the codec
+ * @codec: the codec object
+ *
+ * Returns zero if successful, or a negative error code.
+ */
+int snd_hdac_power_down(struct hdac_device *codec)
+{
+       struct device *dev = &codec->dev;
+
+       return pm_runtime_put_autosuspend(dev);
+}
+EXPORT_SYMBOL_GPL(snd_hdac_power_down);
+
+/**
+ * snd_hdac_power_up_pm - power up the codec
+ * @codec: the codec object
+ *
+ * This function can be called in a recursive code path like init code
+ * which may be called by PM suspend/resume again.  OTOH, if a power-up
+ * call must wake up the sleeper (e.g. in a kctl callback), use
+ * snd_hdac_power_up() instead.
+ *
+ * Returns zero if successful, or a negative error code.
+ */
+int snd_hdac_power_up_pm(struct hdac_device *codec)
+{
+       if (!atomic_inc_not_zero(&codec->in_pm))
+               return snd_hdac_power_up(codec);
+       return 0;
+}
+EXPORT_SYMBOL_GPL(snd_hdac_power_up_pm);
+
+/* like snd_hdac_power_up_pm(), but only increment the pm count when
+ * already powered up.  Returns -1 if not powered up, 1 if incremented
+ * or 0 if unchanged.  Only used in hdac_regmap.c
+ */
+int snd_hdac_keep_power_up(struct hdac_device *codec)
+{
+       if (!atomic_inc_not_zero(&codec->in_pm)) {
+               int ret = pm_runtime_get_if_active(&codec->dev);
+               if (!ret)
+                       return -1;
+               if (ret < 0)
+                       return 0;
+       }
+       return 1;
+}
+
+/**
+ * snd_hdac_power_down_pm - power down the codec
+ * @codec: the codec object
+ *
+ * Like snd_hdac_power_up_pm(), this function is used in a recursive
+ * code path like init code which may be called by PM suspend/resume again.
+ *
+ * Returns zero if successful, or a negative error code.
+ */
+int snd_hdac_power_down_pm(struct hdac_device *codec)
+{
+       if (atomic_dec_if_positive(&codec->in_pm) < 0)
+               return snd_hdac_power_down(codec);
+       return 0;
+}
+EXPORT_SYMBOL_GPL(snd_hdac_power_down_pm);
+#endif
+
+/* codec vendor labels */
+struct hda_vendor_id {
+       unsigned int id;
+       const char *name;
+};
+
+static const struct hda_vendor_id hda_vendor_ids[] = {
+       { 0x0014, "Loongson" },
+       { 0x1002, "ATI" },
+       { 0x1013, "Cirrus Logic" },
+       { 0x1057, "Motorola" },
+       { 0x1095, "Silicon Image" },
+       { 0x10de, "Nvidia" },
+       { 0x10ec, "Realtek" },
+       { 0x1102, "Creative" },
+       { 0x1106, "VIA" },
+       { 0x111d, "IDT" },
+       { 0x11c1, "LSI" },
+       { 0x11d4, "Analog Devices" },
+       { 0x13f6, "C-Media" },
+       { 0x14f1, "Conexant" },
+       { 0x17e8, "Chrontel" },
+       { 0x1854, "LG" },
+       { 0x19e5, "Huawei" },
+       { 0x1aec, "Wolfson Microelectronics" },
+       { 0x1af4, "QEMU" },
+       { 0x1fa8, "Senarytech" },
+       { 0x434d, "C-Media" },
+       { 0x8086, "Intel" },
+       { 0x8384, "SigmaTel" },
+       {} /* terminator */
+};
+
+/* store the codec vendor name */
+static int get_codec_vendor_name(struct hdac_device *codec)
+{
+       const struct hda_vendor_id *c;
+       u16 vendor_id = codec->vendor_id >> 16;
+
+       for (c = hda_vendor_ids; c->id; c++) {
+               if (c->id == vendor_id) {
+                       codec->vendor_name = kstrdup(c->name, GFP_KERNEL);
+                       return codec->vendor_name ? 0 : -ENOMEM;
+               }
+       }
+
+       codec->vendor_name = kasprintf(GFP_KERNEL, "Generic %04x", vendor_id);
+       return codec->vendor_name ? 0 : -ENOMEM;
+}
+
+/*
+ * stream formats
+ */
+struct hda_rate_tbl {
+       unsigned int hz;
+       unsigned int alsa_bits;
+       unsigned int hda_fmt;
+};
+
+/* rate = base * mult / div */
+#define HDA_RATE(base, mult, div) \
+       (AC_FMT_BASE_##base##K | (((mult) - 1) << AC_FMT_MULT_SHIFT) | \
+        (((div) - 1) << AC_FMT_DIV_SHIFT))
+
+static const struct hda_rate_tbl rate_bits[] = {
+       /* rate in Hz, ALSA rate bitmask, HDA format value */
+
+       /* autodetected value used in snd_hda_query_supported_pcm */
+       { 8000, SNDRV_PCM_RATE_8000, HDA_RATE(48, 1, 6) },
+       { 11025, SNDRV_PCM_RATE_11025, HDA_RATE(44, 1, 4) },
+       { 16000, SNDRV_PCM_RATE_16000, HDA_RATE(48, 1, 3) },
+       { 22050, SNDRV_PCM_RATE_22050, HDA_RATE(44, 1, 2) },
+       { 32000, SNDRV_PCM_RATE_32000, HDA_RATE(48, 2, 3) },
+       { 44100, SNDRV_PCM_RATE_44100, HDA_RATE(44, 1, 1) },
+       { 48000, SNDRV_PCM_RATE_48000, HDA_RATE(48, 1, 1) },
+       { 88200, SNDRV_PCM_RATE_88200, HDA_RATE(44, 2, 1) },
+       { 96000, SNDRV_PCM_RATE_96000, HDA_RATE(48, 2, 1) },
+       { 176400, SNDRV_PCM_RATE_176400, HDA_RATE(44, 4, 1) },
+       { 192000, SNDRV_PCM_RATE_192000, HDA_RATE(48, 4, 1) },
+#define AC_PAR_PCM_RATE_BITS   11
+       /* up to bits 10, 384kHZ isn't supported properly */
+
+       /* not autodetected value */
+       { 9600, SNDRV_PCM_RATE_KNOT, HDA_RATE(48, 1, 5) },
+
+       { 0 } /* terminator */
+};
+
+static snd_pcm_format_t snd_hdac_format_normalize(snd_pcm_format_t format)
+{
+       switch (format) {
+       case SNDRV_PCM_FORMAT_S20_LE:
+       case SNDRV_PCM_FORMAT_S24_LE:
+               return SNDRV_PCM_FORMAT_S32_LE;
+
+       case SNDRV_PCM_FORMAT_U20_LE:
+       case SNDRV_PCM_FORMAT_U24_LE:
+               return SNDRV_PCM_FORMAT_U32_LE;
+
+       case SNDRV_PCM_FORMAT_S20_BE:
+       case SNDRV_PCM_FORMAT_S24_BE:
+               return SNDRV_PCM_FORMAT_S32_BE;
+
+       case SNDRV_PCM_FORMAT_U20_BE:
+       case SNDRV_PCM_FORMAT_U24_BE:
+               return SNDRV_PCM_FORMAT_U32_BE;
+
+       default:
+               return format;
+       }
+}
+
+/**
+ * snd_hdac_stream_format_bits - obtain bits per sample value.
+ * @format:    the PCM format.
+ * @subformat: the PCM subformat.
+ * @maxbits:   the maximum bits per sample.
+ *
+ * Return: The number of bits per sample.
+ */
+unsigned int snd_hdac_stream_format_bits(snd_pcm_format_t format, snd_pcm_subformat_t subformat,
+                                        unsigned int maxbits)
+{
+       struct snd_pcm_hw_params params;
+       unsigned int bits;
+
+       memset(&params, 0, sizeof(params));
+
+       params_set_format(&params, snd_hdac_format_normalize(format));
+       snd_mask_set(hw_param_mask(&params, SNDRV_PCM_HW_PARAM_SUBFORMAT),
+                    (__force unsigned int)subformat);
+
+       bits = snd_pcm_hw_params_bits(&params);
+       if (maxbits)
+               return min(bits, maxbits);
+       return bits;
+}
+EXPORT_SYMBOL_GPL(snd_hdac_stream_format_bits);
+
+/**
+ * snd_hdac_stream_format - convert format parameters to SDxFMT value.
+ * @channels:  the number of channels.
+ * @bits:      bits per sample.
+ * @rate:      the sample rate.
+ *
+ * Return: The format bitset or zero if invalid.
+ */
+unsigned int snd_hdac_stream_format(unsigned int channels, unsigned int bits, unsigned int rate)
+{
+       unsigned int val = 0;
+       int i;
+
+       for (i = 0; rate_bits[i].hz; i++) {
+               if (rate_bits[i].hz == rate) {
+                       val = rate_bits[i].hda_fmt;
+                       break;
+               }
+       }
+
+       if (!rate_bits[i].hz)
+               return 0;
+
+       if (channels == 0 || channels > 16)
+               return 0;
+       val |= channels - 1;
+
+       switch (bits) {
+       case 8:
+               val |= AC_FMT_BITS_8;
+               break;
+       case 16:
+               val |= AC_FMT_BITS_16;
+               break;
+       case 20:
+               val |= AC_FMT_BITS_20;
+               break;
+       case 24:
+               val |= AC_FMT_BITS_24;
+               break;
+       case 32:
+               val |= AC_FMT_BITS_32;
+               break;
+       default:
+               return 0;
+       }
+
+       return val;
+}
+EXPORT_SYMBOL_GPL(snd_hdac_stream_format);
+
+/**
+ * snd_hdac_spdif_stream_format - convert format parameters to SDxFMT value.
+ * @channels:  the number of channels.
+ * @bits:      bits per sample.
+ * @rate:      the sample rate.
+ * @spdif_ctls:        HD-audio SPDIF status bits (0 if irrelevant).
+ *
+ * Return: The format bitset or zero if invalid.
+ */
+unsigned int snd_hdac_spdif_stream_format(unsigned int channels, unsigned int bits,
+                                         unsigned int rate, unsigned short spdif_ctls)
+{
+       unsigned int val = snd_hdac_stream_format(channels, bits, rate);
+
+       if (val && spdif_ctls & AC_DIG1_NONAUDIO)
+               val |= AC_FMT_TYPE_NON_PCM;
+
+       return val;
+}
+EXPORT_SYMBOL_GPL(snd_hdac_spdif_stream_format);
+
+static unsigned int query_pcm_param(struct hdac_device *codec, hda_nid_t nid)
+{
+       unsigned int val = 0;
+
+       if (nid != codec->afg &&
+           (snd_hdac_get_wcaps(codec, nid) & AC_WCAP_FORMAT_OVRD))
+               val = snd_hdac_read_parm(codec, nid, AC_PAR_PCM);
+       if (!val || val == -1)
+               val = snd_hdac_read_parm(codec, codec->afg, AC_PAR_PCM);
+       if (!val || val == -1)
+               return 0;
+       return val;
+}
+
+static unsigned int query_stream_param(struct hdac_device *codec, hda_nid_t nid)
+{
+       unsigned int streams = snd_hdac_read_parm(codec, nid, AC_PAR_STREAM);
+
+       if (!streams || streams == -1)
+               streams = snd_hdac_read_parm(codec, codec->afg, AC_PAR_STREAM);
+       if (!streams || streams == -1)
+               return 0;
+       return streams;
+}
+
+/**
+ * snd_hdac_query_supported_pcm - query the supported PCM rates and formats
+ * @codec: the codec object
+ * @nid: NID to query
+ * @ratesp: the pointer to store the detected rate bitflags
+ * @formatsp: the pointer to store the detected formats
+ * @subformatsp: the pointer to store the detected subformats for S32_LE format
+ * @bpsp: the pointer to store the detected format widths
+ *
+ * Queries the supported PCM rates and formats.  The NULL @ratesp, @formatsp,
+ * @subformatsp or @bpsp argument is ignored.
+ *
+ * Returns 0 if successful, otherwise a negative error code.
+ */
+int snd_hdac_query_supported_pcm(struct hdac_device *codec, hda_nid_t nid,
+                                u32 *ratesp, u64 *formatsp, u32 *subformatsp,
+                                unsigned int *bpsp)
+{
+       unsigned int i, val, wcaps;
+
+       wcaps = snd_hdac_get_wcaps(codec, nid);
+       val = query_pcm_param(codec, nid);
+
+       if (ratesp) {
+               u32 rates = 0;
+               for (i = 0; i < AC_PAR_PCM_RATE_BITS; i++) {
+                       if (val & (1 << i))
+                               rates |= rate_bits[i].alsa_bits;
+               }
+               if (rates == 0) {
+                       dev_err(&codec->dev,
+                               "rates == 0 (nid=0x%x, val=0x%x, ovrd=%i)\n",
+                               nid, val,
+                               (wcaps & AC_WCAP_FORMAT_OVRD) ? 1 : 0);
+                       return -EIO;
+               }
+               *ratesp = rates;
+       }
+
+       if (formatsp || subformatsp || bpsp) {
+               unsigned int streams, bps;
+               u32 subformats = 0;
+               u64 formats = 0;
+
+               streams = query_stream_param(codec, nid);
+               if (!streams)
+                       return -EIO;
+
+               bps = 0;
+               if (streams & AC_SUPFMT_PCM) {
+                       if (val & AC_SUPPCM_BITS_8) {
+                               formats |= SNDRV_PCM_FMTBIT_U8;
+                               bps = 8;
+                       }
+                       if (val & AC_SUPPCM_BITS_16) {
+                               formats |= SNDRV_PCM_FMTBIT_S16_LE;
+                               bps = 16;
+                       }
+                       if (val & AC_SUPPCM_BITS_20) {
+                               formats |= SNDRV_PCM_FMTBIT_S32_LE;
+                               subformats |= SNDRV_PCM_SUBFMTBIT_MSBITS_20;
+                               bps = 20;
+                       }
+                       if (val & AC_SUPPCM_BITS_24) {
+                               formats |= SNDRV_PCM_FMTBIT_S32_LE;
+                               subformats |= SNDRV_PCM_SUBFMTBIT_MSBITS_24;
+                               bps = 24;
+                       }
+                       if (val & AC_SUPPCM_BITS_32) {
+                               if (wcaps & AC_WCAP_DIGITAL) {
+                                       formats |= SNDRV_PCM_FMTBIT_IEC958_SUBFRAME_LE;
+                               } else {
+                                       formats |= SNDRV_PCM_FMTBIT_S32_LE;
+                                       subformats |= SNDRV_PCM_SUBFMTBIT_MSBITS_MAX;
+                                       bps = 32;
+                               }
+                       }
+               }
+#if 0 /* FIXME: CS4206 doesn't work, which is the only codec supporting float */
+               if (streams & AC_SUPFMT_FLOAT32) {
+                       formats |= SNDRV_PCM_FMTBIT_FLOAT_LE;
+                       if (!bps)
+                               bps = 32;
+               }
+#endif
+               if (streams == AC_SUPFMT_AC3) {
+                       /* should be exclusive */
+                       /* temporary hack: we have still no proper support
+                        * for the direct AC3 stream...
+                        */
+                       formats |= SNDRV_PCM_FMTBIT_U8;
+                       bps = 8;
+               }
+               if (formats == 0) {
+                       dev_err(&codec->dev,
+                               "formats == 0 (nid=0x%x, val=0x%x, ovrd=%i, streams=0x%x)\n",
+                               nid, val,
+                               (wcaps & AC_WCAP_FORMAT_OVRD) ? 1 : 0,
+                               streams);
+                       return -EIO;
+               }
+               if (formatsp)
+                       *formatsp = formats;
+               if (subformatsp)
+                       *subformatsp = subformats;
+               if (bpsp)
+                       *bpsp = bps;
+       }
+
+       return 0;
+}
+EXPORT_SYMBOL_GPL(snd_hdac_query_supported_pcm);
+
+/**
+ * snd_hdac_is_supported_format - Check the validity of the format
+ * @codec: the codec object
+ * @nid: NID to check
+ * @format: the HD-audio format value to check
+ *
+ * Check whether the given node supports the format value.
+ *
+ * Returns true if supported, false if not.
+ */
+bool snd_hdac_is_supported_format(struct hdac_device *codec, hda_nid_t nid,
+                                 unsigned int format)
+{
+       int i;
+       unsigned int val = 0, rate, stream;
+
+       val = query_pcm_param(codec, nid);
+       if (!val)
+               return false;
+
+       rate = format & 0xff00;
+       for (i = 0; i < AC_PAR_PCM_RATE_BITS; i++)
+               if (rate_bits[i].hda_fmt == rate) {
+                       if (val & (1 << i))
+                               break;
+                       return false;
+               }
+       if (i >= AC_PAR_PCM_RATE_BITS)
+               return false;
+
+       stream = query_stream_param(codec, nid);
+       if (!stream)
+               return false;
+
+       if (stream & AC_SUPFMT_PCM) {
+               switch (format & 0xf0) {
+               case 0x00:
+                       if (!(val & AC_SUPPCM_BITS_8))
+                               return false;
+                       break;
+               case 0x10:
+                       if (!(val & AC_SUPPCM_BITS_16))
+                               return false;
+                       break;
+               case 0x20:
+                       if (!(val & AC_SUPPCM_BITS_20))
+                               return false;
+                       break;
+               case 0x30:
+                       if (!(val & AC_SUPPCM_BITS_24))
+                               return false;
+                       break;
+               case 0x40:
+                       if (!(val & AC_SUPPCM_BITS_32))
+                               return false;
+                       break;
+               default:
+                       return false;
+               }
+       } else {
+               /* FIXME: check for float32 and AC3? */
+       }
+
+       return true;
+}
+EXPORT_SYMBOL_GPL(snd_hdac_is_supported_format);
+
+static unsigned int codec_read(struct hdac_device *hdac, hda_nid_t nid,
+                       int flags, unsigned int verb, unsigned int parm)
+{
+       unsigned int cmd = snd_hdac_make_cmd(hdac, nid, verb, parm);
+       unsigned int res;
+
+       if (snd_hdac_exec_verb(hdac, cmd, flags, &res))
+               return -1;
+
+       return res;
+}
+
+static int codec_write(struct hdac_device *hdac, hda_nid_t nid,
+                       int flags, unsigned int verb, unsigned int parm)
+{
+       unsigned int cmd = snd_hdac_make_cmd(hdac, nid, verb, parm);
+
+       return snd_hdac_exec_verb(hdac, cmd, flags, NULL);
+}
+
+/**
+ * snd_hdac_codec_read - send a command and get the response
+ * @hdac: the HDAC device
+ * @nid: NID to send the command
+ * @flags: optional bit flags
+ * @verb: the verb to send
+ * @parm: the parameter for the verb
+ *
+ * Send a single command and read the corresponding response.
+ *
+ * Returns the obtained response value, or -1 for an error.
+ */
+int snd_hdac_codec_read(struct hdac_device *hdac, hda_nid_t nid,
+                       int flags, unsigned int verb, unsigned int parm)
+{
+       return codec_read(hdac, nid, flags, verb, parm);
+}
+EXPORT_SYMBOL_GPL(snd_hdac_codec_read);
+
+/**
+ * snd_hdac_codec_write - send a single command without waiting for response
+ * @hdac: the HDAC device
+ * @nid: NID to send the command
+ * @flags: optional bit flags
+ * @verb: the verb to send
+ * @parm: the parameter for the verb
+ *
+ * Send a single command without waiting for response.
+ *
+ * Returns 0 if successful, or a negative error code.
+ */
+int snd_hdac_codec_write(struct hdac_device *hdac, hda_nid_t nid,
+                       int flags, unsigned int verb, unsigned int parm)
+{
+       return codec_write(hdac, nid, flags, verb, parm);
+}
+EXPORT_SYMBOL_GPL(snd_hdac_codec_write);
+
+/**
+ * snd_hdac_check_power_state - check whether the actual power state matches
+ * with the target state
+ *
+ * @hdac: the HDAC device
+ * @nid: NID to send the command
+ * @target_state: target state to check for
+ *
+ * Return true if state matches, false if not
+ */
+bool snd_hdac_check_power_state(struct hdac_device *hdac,
+               hda_nid_t nid, unsigned int target_state)
+{
+       unsigned int state = codec_read(hdac, nid, 0,
+                               AC_VERB_GET_POWER_STATE, 0);
+
+       if (state & AC_PWRST_ERROR)
+               return true;
+       state = (state >> 4) & 0x0f;
+       return (state == target_state);
+}
+EXPORT_SYMBOL_GPL(snd_hdac_check_power_state);
+/**
+ * snd_hdac_sync_power_state - wait until actual power state matches
+ * with the target state
+ *
+ * @codec: the HDAC device
+ * @nid: NID to send the command
+ * @power_state: target power state to wait for
+ *
+ * Return power state or PS_ERROR if codec rejects GET verb.
+ */
+unsigned int snd_hdac_sync_power_state(struct hdac_device *codec,
+                       hda_nid_t nid, unsigned int power_state)
+{
+       unsigned long end_time = jiffies + msecs_to_jiffies(500);
+       unsigned int state, actual_state, count;
+
+       for (count = 0; count < 500; count++) {
+               state = snd_hdac_codec_read(codec, nid, 0,
+                               AC_VERB_GET_POWER_STATE, 0);
+               if (state & AC_PWRST_ERROR) {
+                       msleep(20);
+                       break;
+               }
+               actual_state = (state >> 4) & 0x0f;
+               if (actual_state == power_state)
+                       break;
+               if (time_after_eq(jiffies, end_time))
+                       break;
+               /* wait until the codec reachs to the target state */
+               msleep(1);
+       }
+       return state;
+}
+EXPORT_SYMBOL_GPL(snd_hdac_sync_power_state);
diff --git a/sound/hda/core/ext/Makefile b/sound/hda/core/ext/Makefile
new file mode 100644 (file)
index 0000000..85190a7
--- /dev/null
@@ -0,0 +1,4 @@
+# SPDX-License-Identifier: GPL-2.0-only
+snd-hda-ext-core-y := bus.o controller.o stream.o
+
+obj-$(CONFIG_SND_HDA_EXT_CORE) += snd-hda-ext-core.o
diff --git a/sound/hda/core/ext/bus.c b/sound/hda/core/ext/bus.c
new file mode 100644 (file)
index 0000000..6004ea1
--- /dev/null
@@ -0,0 +1,142 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ *  hdac-ext-bus.c - HD-audio extended core bus functions.
+ *
+ *  Copyright (C) 2014-2015 Intel Corp
+ *  Author: Jeeja KP <jeeja.kp@intel.com>
+ *  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ *
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ */
+
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/io.h>
+#include <sound/hdaudio_ext.h>
+
+MODULE_DESCRIPTION("HDA extended core");
+MODULE_LICENSE("GPL v2");
+
+/**
+ * snd_hdac_ext_bus_init - initialize a HD-audio extended bus
+ * @bus: the pointer to HDAC bus object
+ * @dev: device pointer
+ * @ops: bus verb operators
+ * @ext_ops: operators used for ASoC HDA codec drivers
+ *
+ * Returns 0 if successful, or a negative error code.
+ */
+int snd_hdac_ext_bus_init(struct hdac_bus *bus, struct device *dev,
+                       const struct hdac_bus_ops *ops,
+                       const struct hdac_ext_bus_ops *ext_ops)
+{
+       int ret;
+
+       ret = snd_hdac_bus_init(bus, dev, ops);
+       if (ret < 0)
+               return ret;
+
+       bus->ext_ops = ext_ops;
+       /* FIXME:
+        * Currently only one bus is supported, if there is device with more
+        * buses, bus->idx should be greater than 0, but there needs to be a
+        * reliable way to always assign same number.
+        */
+       bus->idx = 0;
+       bus->cmd_dma_state = true;
+
+       return 0;
+}
+EXPORT_SYMBOL_GPL(snd_hdac_ext_bus_init);
+
+/**
+ * snd_hdac_ext_bus_exit - clean up a HD-audio extended bus
+ * @bus: the pointer to HDAC bus object
+ */
+void snd_hdac_ext_bus_exit(struct hdac_bus *bus)
+{
+       snd_hdac_bus_exit(bus);
+       WARN_ON(!list_empty(&bus->hlink_list));
+}
+EXPORT_SYMBOL_GPL(snd_hdac_ext_bus_exit);
+
+/**
+ * snd_hdac_ext_bus_device_remove - remove HD-audio extended codec base devices
+ *
+ * @bus: the pointer to HDAC bus object
+ */
+void snd_hdac_ext_bus_device_remove(struct hdac_bus *bus)
+{
+       struct hdac_device *codec, *__codec;
+       /*
+        * we need to remove all the codec devices objects created in the
+        * snd_hdac_ext_bus_device_init
+        */
+       list_for_each_entry_safe(codec, __codec, &bus->codec_list, list) {
+               snd_hdac_device_unregister(codec);
+               put_device(&codec->dev);
+       }
+}
+EXPORT_SYMBOL_GPL(snd_hdac_ext_bus_device_remove);
+#define dev_to_hdac(dev) (container_of((dev), \
+                       struct hdac_device, dev))
+
+static inline struct hdac_driver *get_hdrv(struct device *dev)
+{
+       struct hdac_driver *hdrv = drv_to_hdac_driver(dev->driver);
+       return hdrv;
+}
+
+static inline struct hdac_device *get_hdev(struct device *dev)
+{
+       struct hdac_device *hdev = dev_to_hdac_dev(dev);
+       return hdev;
+}
+
+static int hda_ext_drv_probe(struct device *dev)
+{
+       return (get_hdrv(dev))->probe(get_hdev(dev));
+}
+
+static int hdac_ext_drv_remove(struct device *dev)
+{
+       return (get_hdrv(dev))->remove(get_hdev(dev));
+}
+
+static void hdac_ext_drv_shutdown(struct device *dev)
+{
+       return (get_hdrv(dev))->shutdown(get_hdev(dev));
+}
+
+/**
+ * snd_hda_ext_driver_register - register a driver for ext hda devices
+ *
+ * @drv: ext hda driver structure
+ */
+int snd_hda_ext_driver_register(struct hdac_driver *drv)
+{
+       drv->type = HDA_DEV_ASOC;
+       drv->driver.bus = &snd_hda_bus_type;
+       /* we use default match */
+
+       if (drv->probe)
+               drv->driver.probe = hda_ext_drv_probe;
+       if (drv->remove)
+               drv->driver.remove = hdac_ext_drv_remove;
+       if (drv->shutdown)
+               drv->driver.shutdown = hdac_ext_drv_shutdown;
+
+       return driver_register(&drv->driver);
+}
+EXPORT_SYMBOL_GPL(snd_hda_ext_driver_register);
+
+/**
+ * snd_hda_ext_driver_unregister - unregister a driver for ext hda devices
+ *
+ * @drv: ext hda driver structure
+ */
+void snd_hda_ext_driver_unregister(struct hdac_driver *drv)
+{
+       driver_unregister(&drv->driver);
+}
+EXPORT_SYMBOL_GPL(snd_hda_ext_driver_unregister);
diff --git a/sound/hda/core/ext/controller.c b/sound/hda/core/ext/controller.c
new file mode 100644 (file)
index 0000000..c847544
--- /dev/null
@@ -0,0 +1,412 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ *  hdac-ext-controller.c - HD-audio extended controller functions.
+ *
+ *  Copyright (C) 2014-2015 Intel Corp
+ *  Author: Jeeja KP <jeeja.kp@intel.com>
+ *  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ *
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ */
+
+#include <linux/bitfield.h>
+#include <linux/delay.h>
+#include <linux/slab.h>
+#include <sound/hda_register.h>
+#include <sound/hdaudio_ext.h>
+
+/*
+ * processing pipe helpers - these helpers are useful for dealing with HDA
+ * new capability of processing pipelines
+ */
+
+/**
+ * snd_hdac_ext_bus_ppcap_enable - enable/disable processing pipe capability
+ * @bus: the pointer to HDAC bus object
+ * @enable: flag to turn on/off the capability
+ */
+void snd_hdac_ext_bus_ppcap_enable(struct hdac_bus *bus, bool enable)
+{
+
+       if (!bus->ppcap) {
+               dev_err(bus->dev, "Address of PP capability is NULL");
+               return;
+       }
+
+       if (enable)
+               snd_hdac_updatel(bus->ppcap, AZX_REG_PP_PPCTL,
+                                AZX_PPCTL_GPROCEN, AZX_PPCTL_GPROCEN);
+       else
+               snd_hdac_updatel(bus->ppcap, AZX_REG_PP_PPCTL,
+                                AZX_PPCTL_GPROCEN, 0);
+}
+EXPORT_SYMBOL_GPL(snd_hdac_ext_bus_ppcap_enable);
+
+/**
+ * snd_hdac_ext_bus_ppcap_int_enable - ppcap interrupt enable/disable
+ * @bus: the pointer to HDAC bus object
+ * @enable: flag to enable/disable interrupt
+ */
+void snd_hdac_ext_bus_ppcap_int_enable(struct hdac_bus *bus, bool enable)
+{
+
+       if (!bus->ppcap) {
+               dev_err(bus->dev, "Address of PP capability is NULL\n");
+               return;
+       }
+
+       if (enable)
+               snd_hdac_updatel(bus->ppcap, AZX_REG_PP_PPCTL,
+                                AZX_PPCTL_PIE, AZX_PPCTL_PIE);
+       else
+               snd_hdac_updatel(bus->ppcap, AZX_REG_PP_PPCTL,
+                                AZX_PPCTL_PIE, 0);
+}
+EXPORT_SYMBOL_GPL(snd_hdac_ext_bus_ppcap_int_enable);
+
+/*
+ * Multilink helpers - these helpers are useful for dealing with HDA
+ * new multilink capability
+ */
+
+/**
+ * snd_hdac_ext_bus_get_ml_capabilities - get multilink capability
+ * @bus: the pointer to HDAC bus object
+ *
+ * This will parse all links and read the mlink capabilities and add them
+ * in hlink_list of extended hdac bus
+ * Note: this will be freed on bus exit by driver
+ */
+int snd_hdac_ext_bus_get_ml_capabilities(struct hdac_bus *bus)
+{
+       int idx;
+       u32 link_count;
+       struct hdac_ext_link *hlink;
+       u32 leptr;
+
+       link_count = readl(bus->mlcap + AZX_REG_ML_MLCD) + 1;
+
+       dev_dbg(bus->dev, "In %s Link count: %d\n", __func__, link_count);
+
+       for (idx = 0; idx < link_count; idx++) {
+               hlink  = kzalloc(sizeof(*hlink), GFP_KERNEL);
+               if (!hlink)
+                       return -ENOMEM;
+               hlink->index = idx;
+               hlink->bus = bus;
+               hlink->ml_addr = bus->mlcap + AZX_ML_BASE +
+                                       (AZX_ML_INTERVAL * idx);
+               hlink->lcaps  = readl(hlink->ml_addr + AZX_REG_ML_LCAP);
+               hlink->lsdiid = readw(hlink->ml_addr + AZX_REG_ML_LSDIID);
+               hlink->slcount = FIELD_GET(AZX_ML_HDA_LCAP_SLCOUNT, hlink->lcaps) + 1;
+
+               if (hdac_ext_link_alt(hlink)) {
+                       leptr = readl(hlink->ml_addr + AZX_REG_ML_LEPTR);
+                       hlink->id = FIELD_GET(AZX_REG_ML_LEPTR_ID, leptr);
+               }
+
+               /* since link in On, update the ref */
+               hlink->ref_count = 1;
+
+               list_add_tail(&hlink->list, &bus->hlink_list);
+       }
+
+       return 0;
+}
+EXPORT_SYMBOL_GPL(snd_hdac_ext_bus_get_ml_capabilities);
+
+/**
+ * snd_hdac_ext_link_free_all- free hdac extended link objects
+ *
+ * @bus: the pointer to HDAC bus object
+ */
+
+void snd_hdac_ext_link_free_all(struct hdac_bus *bus)
+{
+       struct hdac_ext_link *hlink;
+
+       while (!list_empty(&bus->hlink_list)) {
+               hlink = list_first_entry(&bus->hlink_list, struct hdac_ext_link, list);
+               list_del(&hlink->list);
+               kfree(hlink);
+       }
+}
+EXPORT_SYMBOL_GPL(snd_hdac_ext_link_free_all);
+
+struct hdac_ext_link *snd_hdac_ext_bus_get_hlink_by_id(struct hdac_bus *bus, u32 id)
+{
+       struct hdac_ext_link *hlink;
+
+       list_for_each_entry(hlink, &bus->hlink_list, list)
+               if (hdac_ext_link_alt(hlink) && hlink->id == id)
+                       return hlink;
+       return NULL;
+}
+EXPORT_SYMBOL_GPL(snd_hdac_ext_bus_get_hlink_by_id);
+
+/**
+ * snd_hdac_ext_bus_get_hlink_by_addr - get hlink at specified address
+ * @bus: hlink's parent bus device
+ * @addr: codec device address
+ *
+ * Returns hlink object or NULL if matching hlink is not found.
+ */
+struct hdac_ext_link *snd_hdac_ext_bus_get_hlink_by_addr(struct hdac_bus *bus, int addr)
+{
+       struct hdac_ext_link *hlink;
+
+       list_for_each_entry(hlink, &bus->hlink_list, list)
+               if (hlink->lsdiid & (0x1 << addr))
+                       return hlink;
+       return NULL;
+}
+EXPORT_SYMBOL_GPL(snd_hdac_ext_bus_get_hlink_by_addr);
+
+/**
+ * snd_hdac_ext_bus_get_hlink_by_name - get hlink based on codec name
+ * @bus: the pointer to HDAC bus object
+ * @codec_name: codec name
+ */
+struct hdac_ext_link *snd_hdac_ext_bus_get_hlink_by_name(struct hdac_bus *bus,
+                                                        const char *codec_name)
+{
+       int bus_idx, addr;
+
+       if (sscanf(codec_name, "ehdaudio%dD%d", &bus_idx, &addr) != 2)
+               return NULL;
+       if (bus->idx != bus_idx)
+               return NULL;
+       if (addr < 0 || addr > 31)
+               return NULL;
+
+       return snd_hdac_ext_bus_get_hlink_by_addr(bus, addr);
+}
+EXPORT_SYMBOL_GPL(snd_hdac_ext_bus_get_hlink_by_name);
+
+static int check_hdac_link_power_active(struct hdac_ext_link *hlink, bool enable)
+{
+       int timeout;
+       u32 val;
+       int mask = (1 << AZX_ML_LCTL_CPA_SHIFT);
+
+       udelay(3);
+       timeout = 150;
+
+       do {
+               val = readl(hlink->ml_addr + AZX_REG_ML_LCTL);
+               if (enable) {
+                       if (((val & mask) >> AZX_ML_LCTL_CPA_SHIFT))
+                               return 0;
+               } else {
+                       if (!((val & mask) >> AZX_ML_LCTL_CPA_SHIFT))
+                               return 0;
+               }
+               udelay(3);
+       } while (--timeout);
+
+       return -EIO;
+}
+
+/**
+ * snd_hdac_ext_bus_link_power_up -power up hda link
+ * @hlink: HD-audio extended link
+ */
+int snd_hdac_ext_bus_link_power_up(struct hdac_ext_link *hlink)
+{
+       snd_hdac_updatel(hlink->ml_addr, AZX_REG_ML_LCTL,
+                        AZX_ML_LCTL_SPA, AZX_ML_LCTL_SPA);
+
+       return check_hdac_link_power_active(hlink, true);
+}
+EXPORT_SYMBOL_GPL(snd_hdac_ext_bus_link_power_up);
+
+/**
+ * snd_hdac_ext_bus_link_power_down -power down hda link
+ * @hlink: HD-audio extended link
+ */
+int snd_hdac_ext_bus_link_power_down(struct hdac_ext_link *hlink)
+{
+       snd_hdac_updatel(hlink->ml_addr, AZX_REG_ML_LCTL, AZX_ML_LCTL_SPA, 0);
+
+       return check_hdac_link_power_active(hlink, false);
+}
+EXPORT_SYMBOL_GPL(snd_hdac_ext_bus_link_power_down);
+
+/**
+ * snd_hdac_ext_bus_link_power_up_all -power up all hda link
+ * @bus: the pointer to HDAC bus object
+ */
+int snd_hdac_ext_bus_link_power_up_all(struct hdac_bus *bus)
+{
+       struct hdac_ext_link *hlink = NULL;
+       int ret;
+
+       list_for_each_entry(hlink, &bus->hlink_list, list) {
+               ret = snd_hdac_ext_bus_link_power_up(hlink);
+               if (ret < 0)
+                       return ret;
+       }
+
+       return 0;
+}
+EXPORT_SYMBOL_GPL(snd_hdac_ext_bus_link_power_up_all);
+
+/**
+ * snd_hdac_ext_bus_link_power_down_all -power down all hda link
+ * @bus: the pointer to HDAC bus object
+ */
+int snd_hdac_ext_bus_link_power_down_all(struct hdac_bus *bus)
+{
+       struct hdac_ext_link *hlink = NULL;
+       int ret;
+
+       list_for_each_entry(hlink, &bus->hlink_list, list) {
+               ret = snd_hdac_ext_bus_link_power_down(hlink);
+               if (ret < 0)
+                       return ret;
+       }
+
+       return 0;
+}
+EXPORT_SYMBOL_GPL(snd_hdac_ext_bus_link_power_down_all);
+
+/**
+ * snd_hdac_ext_bus_link_set_stream_id - maps stream id to link output
+ * @link: HD-audio ext link to set up
+ * @stream: stream id
+ */
+void snd_hdac_ext_bus_link_set_stream_id(struct hdac_ext_link *link,
+                                        int stream)
+{
+       snd_hdac_updatew(link->ml_addr, AZX_REG_ML_LOSIDV, (1 << stream), 1 << stream);
+}
+EXPORT_SYMBOL_GPL(snd_hdac_ext_bus_link_set_stream_id);
+
+/**
+ * snd_hdac_ext_bus_link_clear_stream_id - maps stream id to link output
+ * @link: HD-audio ext link to set up
+ * @stream: stream id
+ */
+void snd_hdac_ext_bus_link_clear_stream_id(struct hdac_ext_link *link,
+                                          int stream)
+{
+       snd_hdac_updatew(link->ml_addr, AZX_REG_ML_LOSIDV, (1 << stream), 0);
+}
+EXPORT_SYMBOL_GPL(snd_hdac_ext_bus_link_clear_stream_id);
+
+int snd_hdac_ext_bus_link_get(struct hdac_bus *bus,
+                               struct hdac_ext_link *hlink)
+{
+       unsigned long codec_mask;
+       int ret = 0;
+
+       mutex_lock(&bus->lock);
+
+       /*
+        * if we move from 0 to 1, count will be 1 so power up this link
+        * as well, also check the dma status and trigger that
+        */
+       if (++hlink->ref_count == 1) {
+               if (!bus->cmd_dma_state) {
+                       snd_hdac_bus_init_cmd_io(bus);
+                       bus->cmd_dma_state = true;
+               }
+
+               ret = snd_hdac_ext_bus_link_power_up(hlink);
+
+               /*
+                * clear the register to invalidate all the output streams
+                */
+               snd_hdac_updatew(hlink->ml_addr, AZX_REG_ML_LOSIDV,
+                                AZX_ML_LOSIDV_STREAM_MASK, 0);
+               /*
+                *  wait for 521usec for codec to report status
+                *  HDA spec section 4.3 - Codec Discovery
+                */
+               udelay(521);
+               codec_mask = snd_hdac_chip_readw(bus, STATESTS);
+               dev_dbg(bus->dev, "codec_mask = 0x%lx\n", codec_mask);
+               snd_hdac_chip_writew(bus, STATESTS, codec_mask);
+               if (!bus->codec_mask)
+                       bus->codec_mask = codec_mask;
+       }
+
+       mutex_unlock(&bus->lock);
+       return ret;
+}
+EXPORT_SYMBOL_GPL(snd_hdac_ext_bus_link_get);
+
+int snd_hdac_ext_bus_link_put(struct hdac_bus *bus,
+                             struct hdac_ext_link *hlink)
+{
+       int ret = 0;
+       struct hdac_ext_link *hlink_tmp;
+       bool link_up = false;
+
+       mutex_lock(&bus->lock);
+
+       /*
+        * if we move from 1 to 0, count will be 0
+        * so power down this link as well
+        */
+       if (--hlink->ref_count == 0) {
+               ret = snd_hdac_ext_bus_link_power_down(hlink);
+
+               /*
+                * now check if all links are off, if so turn off
+                * cmd dma as well
+                */
+               list_for_each_entry(hlink_tmp, &bus->hlink_list, list) {
+                       if (hlink_tmp->ref_count) {
+                               link_up = true;
+                               break;
+                       }
+               }
+
+               if (!link_up) {
+                       snd_hdac_bus_stop_cmd_io(bus);
+                       bus->cmd_dma_state = false;
+               }
+       }
+
+       mutex_unlock(&bus->lock);
+       return ret;
+}
+EXPORT_SYMBOL_GPL(snd_hdac_ext_bus_link_put);
+
+static void hdac_ext_codec_link_up(struct hdac_device *codec)
+{
+       const char *devname = dev_name(&codec->dev);
+       struct hdac_ext_link *hlink =
+               snd_hdac_ext_bus_get_hlink_by_name(codec->bus, devname);
+
+       if (hlink)
+               snd_hdac_ext_bus_link_get(codec->bus, hlink);
+}
+
+static void hdac_ext_codec_link_down(struct hdac_device *codec)
+{
+       const char *devname = dev_name(&codec->dev);
+       struct hdac_ext_link *hlink =
+               snd_hdac_ext_bus_get_hlink_by_name(codec->bus, devname);
+
+       if (hlink)
+               snd_hdac_ext_bus_link_put(codec->bus, hlink);
+}
+
+void snd_hdac_ext_bus_link_power(struct hdac_device *codec, bool enable)
+{
+       struct hdac_bus *bus = codec->bus;
+       bool oldstate = test_bit(codec->addr, &bus->codec_powered);
+
+       if (enable == oldstate)
+               return;
+
+       snd_hdac_bus_link_power(codec, enable);
+
+       if (enable)
+               hdac_ext_codec_link_up(codec);
+       else
+               hdac_ext_codec_link_down(codec);
+}
+EXPORT_SYMBOL_GPL(snd_hdac_ext_bus_link_power);
diff --git a/sound/hda/core/ext/stream.c b/sound/hda/core/ext/stream.c
new file mode 100644 (file)
index 0000000..a3ac738
--- /dev/null
@@ -0,0 +1,453 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ *  hdac-ext-stream.c - HD-audio extended stream operations.
+ *
+ *  Copyright (C) 2015 Intel Corp
+ *  Author: Jeeja KP <jeeja.kp@intel.com>
+ *  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ *
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ */
+
+#include <linux/delay.h>
+#include <linux/pci.h>
+#include <linux/pci_ids.h>
+#include <linux/slab.h>
+#include <sound/pcm.h>
+#include <sound/hda_register.h>
+#include <sound/hdaudio_ext.h>
+#include <sound/compress_driver.h>
+
+/**
+ * snd_hdac_ext_host_stream_setup - Setup a HOST stream.
+ * @hext_stream: HDAudio stream to set up.
+ * @code_loading: Whether the stream is for PCM or code-loading.
+ *
+ * Return: Zero on success or negative error code.
+ */
+int snd_hdac_ext_host_stream_setup(struct hdac_ext_stream *hext_stream, bool code_loading)
+{
+       return hext_stream->host_setup(hdac_stream(hext_stream), code_loading);
+}
+EXPORT_SYMBOL_GPL(snd_hdac_ext_host_stream_setup);
+
+/**
+ * snd_hdac_apl_host_stream_setup - Setup a HOST stream following procedure
+ *                                  recommended for ApolloLake devices.
+ * @hstream: HDAudio stream to set up.
+ * @code_loading: Whether the stream is for PCM or code-loading.
+ *
+ * Return: Zero on success or negative error code.
+ */
+static int snd_hdac_apl_host_stream_setup(struct hdac_stream *hstream, bool code_loading)
+{
+       struct hdac_ext_stream *hext_stream = stream_to_hdac_ext_stream(hstream);
+       int ret;
+
+       snd_hdac_ext_stream_decouple(hstream->bus, hext_stream, false);
+       ret = snd_hdac_stream_setup(hstream, code_loading);
+       snd_hdac_ext_stream_decouple(hstream->bus, hext_stream, true);
+
+       return ret;
+}
+
+/**
+ * snd_hdac_ext_stream_init - initialize each stream (aka device)
+ * @bus: HD-audio core bus
+ * @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
+ *
+ * initialize the stream, if ppcap is enabled then init those and then
+ * invoke hdac stream initialization routine
+ */
+static void snd_hdac_ext_stream_init(struct hdac_bus *bus,
+                                    struct hdac_ext_stream *hext_stream,
+                                    int idx, int direction, int tag)
+{
+       if (bus->ppcap) {
+               hext_stream->pphc_addr = bus->ppcap + AZX_PPHC_BASE +
+                               AZX_PPHC_INTERVAL * idx;
+
+               hext_stream->pplc_addr = bus->ppcap + AZX_PPLC_BASE +
+                               AZX_PPLC_MULTI * bus->num_streams +
+                               AZX_PPLC_INTERVAL * idx;
+       }
+
+       hext_stream->decoupled = false;
+       snd_hdac_stream_init(bus, &hext_stream->hstream, idx, direction, tag);
+}
+
+/**
+ * snd_hdac_ext_stream_init_all - create and initialize the stream objects
+ *   for an extended hda bus
+ * @bus: HD-audio core bus
+ * @start_idx: start index for streams
+ * @num_stream: number of streams to initialize
+ * @dir: direction of streams
+ */
+int snd_hdac_ext_stream_init_all(struct hdac_bus *bus, int start_idx,
+                                int num_stream, int dir)
+{
+       struct pci_dev *pci = to_pci_dev(bus->dev);
+       int (*setup_op)(struct hdac_stream *, bool);
+       int stream_tag = 0;
+       int i, tag, idx = start_idx;
+
+       if (pci->device == PCI_DEVICE_ID_INTEL_HDA_APL)
+               setup_op = snd_hdac_apl_host_stream_setup;
+       else
+               setup_op = snd_hdac_stream_setup;
+
+       for (i = 0; i < num_stream; i++) {
+               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, hext_stream, idx, dir, tag);
+               idx++;
+               hext_stream->host_setup = setup_op;
+       }
+
+       return 0;
+
+}
+EXPORT_SYMBOL_GPL(snd_hdac_ext_stream_init_all);
+
+/**
+ * snd_hdac_ext_stream_free_all - free hdac extended stream objects
+ *
+ * @bus: HD-audio core bus
+ */
+void snd_hdac_ext_stream_free_all(struct hdac_bus *bus)
+{
+       struct hdac_stream *s, *_s;
+       struct hdac_ext_stream *hext_stream;
+
+       list_for_each_entry_safe(s, _s, &bus->stream_list, list) {
+               hext_stream = stream_to_hdac_ext_stream(s);
+               snd_hdac_ext_stream_decouple(bus, hext_stream, false);
+               list_del(&s->list);
+               kfree(hext_stream);
+       }
+}
+EXPORT_SYMBOL_GPL(snd_hdac_ext_stream_free_all);
+
+void snd_hdac_ext_stream_decouple_locked(struct hdac_bus *bus,
+                                        struct hdac_ext_stream *hext_stream,
+                                        bool decouple)
+{
+       struct hdac_stream *hstream = &hext_stream->hstream;
+       u32 val;
+       int mask = AZX_PPCTL_PROCEN(hstream->index);
+
+       val = readw(bus->ppcap + AZX_REG_PP_PPCTL) & mask;
+
+       if (decouple && !val)
+               snd_hdac_updatel(bus->ppcap, AZX_REG_PP_PPCTL, mask, mask);
+       else if (!decouple && val)
+               snd_hdac_updatel(bus->ppcap, AZX_REG_PP_PPCTL, mask, 0);
+
+       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
+ * @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 *hext_stream, bool decouple)
+{
+       spin_lock_irq(&bus->reg_lock);
+       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_stream_start - start a stream
+ * @hext_stream: HD-audio ext core stream to start
+ */
+void snd_hdac_ext_stream_start(struct hdac_ext_stream *hext_stream)
+{
+       snd_hdac_updatel(hext_stream->pplc_addr, AZX_REG_PPLCCTL,
+                        AZX_PPLCCTL_RUN, AZX_PPLCCTL_RUN);
+}
+EXPORT_SYMBOL_GPL(snd_hdac_ext_stream_start);
+
+/**
+ * snd_hdac_ext_stream_clear - stop a stream DMA
+ * @hext_stream: HD-audio ext core stream to stop
+ */
+void snd_hdac_ext_stream_clear(struct hdac_ext_stream *hext_stream)
+{
+       snd_hdac_updatel(hext_stream->pplc_addr, AZX_REG_PPLCCTL, AZX_PPLCCTL_RUN, 0);
+}
+EXPORT_SYMBOL_GPL(snd_hdac_ext_stream_clear);
+
+/**
+ * snd_hdac_ext_stream_reset - reset a stream
+ * @hext_stream: HD-audio ext core stream to reset
+ */
+void snd_hdac_ext_stream_reset(struct hdac_ext_stream *hext_stream)
+{
+       unsigned char val;
+       int timeout;
+
+       snd_hdac_ext_stream_clear(hext_stream);
+
+       snd_hdac_updatel(hext_stream->pplc_addr, AZX_REG_PPLCCTL,
+                        AZX_PPLCCTL_STRST, AZX_PPLCCTL_STRST);
+       udelay(3);
+       timeout = 50;
+       do {
+               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, 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(hext_stream->pplc_addr + AZX_REG_PPLCCTL) & AZX_PPLCCTL_STRST;
+               if (!val)
+                       break;
+               udelay(3);
+       } while (--timeout);
+
+}
+EXPORT_SYMBOL_GPL(snd_hdac_ext_stream_reset);
+
+/**
+ * snd_hdac_ext_stream_setup -  set up the SD for streaming
+ * @hext_stream: HD-audio ext core stream to set up
+ * @fmt: stream format
+ */
+int snd_hdac_ext_stream_setup(struct hdac_ext_stream *hext_stream, int fmt)
+{
+       struct hdac_stream *hstream = &hext_stream->hstream;
+       unsigned int val;
+
+       /* make sure the run bit is zero for SD */
+       snd_hdac_ext_stream_clear(hext_stream);
+       /* program the stream_tag */
+       val = readl(hext_stream->pplc_addr + AZX_REG_PPLCCTL);
+       val = (val & ~AZX_PPLCCTL_STRM_MASK) |
+               (hstream->stream_tag << AZX_PPLCCTL_STRM_SHIFT);
+       writel(val, hext_stream->pplc_addr + AZX_REG_PPLCCTL);
+
+       /* program the stream format */
+       writew(fmt, hext_stream->pplc_addr + AZX_REG_PPLCFMT);
+
+       return 0;
+}
+EXPORT_SYMBOL_GPL(snd_hdac_ext_stream_setup);
+
+static struct hdac_ext_stream *
+hdac_ext_link_dma_stream_assign(struct hdac_bus *bus,
+                               struct snd_pcm_substream *substream)
+{
+       struct hdac_ext_stream *res = NULL;
+       struct hdac_stream *hstream = NULL;
+
+       if (!bus->ppcap) {
+               dev_err(bus->dev, "stream type not supported\n");
+               return NULL;
+       }
+
+       spin_lock_irq(&bus->reg_lock);
+       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 link stream is available */
+               if (!hext_stream->link_locked) {
+                       res = hext_stream;
+                       break;
+               }
+
+       }
+       if (res) {
+               snd_hdac_ext_stream_decouple_locked(bus, res, true);
+               res->link_locked = 1;
+               res->link_substream = substream;
+       }
+       spin_unlock_irq(&bus->reg_lock);
+       return res;
+}
+
+static struct hdac_ext_stream *
+hdac_ext_host_dma_stream_assign(struct hdac_bus *bus,
+                               struct snd_pcm_substream *substream)
+{
+       struct hdac_ext_stream *res = NULL;
+       struct hdac_stream *hstream = NULL;
+
+       if (!bus->ppcap) {
+               dev_err(bus->dev, "stream type not supported\n");
+               return NULL;
+       }
+
+       spin_lock_irq(&bus->reg_lock);
+       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 (!hstream->opened) {
+                       res = hext_stream;
+                       break;
+               }
+       }
+       if (res) {
+               snd_hdac_ext_stream_decouple_locked(bus, res, true);
+               res->hstream.opened = 1;
+               res->hstream.running = 0;
+               res->hstream.substream = substream;
+       }
+       spin_unlock_irq(&bus->reg_lock);
+
+       return res;
+}
+
+/**
+ * snd_hdac_ext_stream_assign - assign a stream for the PCM
+ * @bus: HD-audio core bus
+ * @substream: PCM substream to assign
+ * @type: type of stream (coupled, host or link stream)
+ *
+ * This assigns the stream based on the type (coupled/host/link), for the
+ * given PCM substream, assigns it and returns the stream object
+ *
+ * coupled: Looks for an unused stream
+ * host: Looks for an unused decoupled host stream
+ * link: Looks for an unused decoupled link stream
+ *
+ * If no stream is free, returns NULL. The function tries to keep using
+ * the same stream object when it's used beforehand.  when a stream is
+ * decoupled, it becomes a host stream and link stream.
+ */
+struct hdac_ext_stream *snd_hdac_ext_stream_assign(struct hdac_bus *bus,
+                                          struct snd_pcm_substream *substream,
+                                          int type)
+{
+       struct hdac_ext_stream *hext_stream = NULL;
+       struct hdac_stream *hstream = NULL;
+
+       switch (type) {
+       case HDAC_EXT_STREAM_TYPE_COUPLED:
+               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_dma_stream_assign(bus, substream);
+
+       case HDAC_EXT_STREAM_TYPE_LINK:
+               return hdac_ext_link_dma_stream_assign(bus, substream);
+
+       default:
+               return NULL;
+       }
+}
+EXPORT_SYMBOL_GPL(snd_hdac_ext_stream_assign);
+
+/**
+ * snd_hdac_ext_stream_release - release the assigned stream
+ * @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 *hext_stream, int type)
+{
+       struct hdac_bus *bus = hext_stream->hstream.bus;
+
+       switch (type) {
+       case HDAC_EXT_STREAM_TYPE_COUPLED:
+               snd_hdac_stream_release(&hext_stream->hstream);
+               break;
+
+       case HDAC_EXT_STREAM_TYPE_HOST:
+               spin_lock_irq(&bus->reg_lock);
+               /* couple link only if not in use */
+               if (!hext_stream->link_locked)
+                       snd_hdac_ext_stream_decouple_locked(bus, hext_stream, false);
+               snd_hdac_stream_release_locked(&hext_stream->hstream);
+               spin_unlock_irq(&bus->reg_lock);
+               break;
+
+       case HDAC_EXT_STREAM_TYPE_LINK:
+               spin_lock_irq(&bus->reg_lock);
+               /* couple host only if not in use */
+               if (!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;
+
+       default:
+               dev_dbg(bus->dev, "Invalid type %d\n", type);
+       }
+
+}
+EXPORT_SYMBOL_GPL(snd_hdac_ext_stream_release);
+
+/**
+ * snd_hdac_ext_cstream_assign - assign a host stream for compress
+ * @bus: HD-audio core bus
+ * @cstream: Compress stream to assign
+ *
+ * Assign an unused host stream for the given compress stream.
+ * If no stream is free, NULL is returned. Stream is decoupled
+ * before assignment.
+ */
+struct hdac_ext_stream *snd_hdac_ext_cstream_assign(struct hdac_bus *bus,
+                                                   struct snd_compr_stream *cstream)
+{
+       struct hdac_ext_stream *res = NULL;
+       struct hdac_stream *hstream;
+
+       spin_lock_irq(&bus->reg_lock);
+       list_for_each_entry(hstream, &bus->stream_list, list) {
+               struct hdac_ext_stream *hext_stream = stream_to_hdac_ext_stream(hstream);
+
+               if (hstream->direction != cstream->direction)
+                       continue;
+
+               if (!hstream->opened) {
+                       res = hext_stream;
+                       break;
+               }
+       }
+
+       if (res) {
+               snd_hdac_ext_stream_decouple_locked(bus, res, true);
+               res->hstream.opened = 1;
+               res->hstream.running = 0;
+               res->hstream.cstream = cstream;
+       }
+       spin_unlock_irq(&bus->reg_lock);
+
+       return res;
+}
+EXPORT_SYMBOL_GPL(snd_hdac_ext_cstream_assign);
diff --git a/sound/hda/core/hda_bus_type.c b/sound/hda/core/hda_bus_type.c
new file mode 100644 (file)
index 0000000..eb72a7a
--- /dev/null
@@ -0,0 +1,97 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * HD-audio bus
+ */
+#include <linux/init.h>
+#include <linux/device.h>
+#include <linux/module.h>
+#include <linux/mod_devicetable.h>
+#include <linux/export.h>
+#include <sound/hdaudio.h>
+
+MODULE_DESCRIPTION("HD-audio bus");
+MODULE_LICENSE("GPL");
+
+/**
+ * hdac_get_device_id - gets the hdac device id entry
+ * @hdev: HD-audio core device
+ * @drv: HD-audio codec driver
+ *
+ * Compares the hdac device vendor_id and revision_id to the hdac_device
+ * driver id_table and returns the matching device id entry.
+ */
+const struct hda_device_id *
+hdac_get_device_id(struct hdac_device *hdev, const struct hdac_driver *drv)
+{
+       if (drv->id_table) {
+               const struct hda_device_id *id  = drv->id_table;
+
+               while (id->vendor_id) {
+                       if (hdev->vendor_id == id->vendor_id &&
+                               (!id->rev_id || id->rev_id == hdev->revision_id))
+                               return id;
+                       id++;
+               }
+       }
+
+       return NULL;
+}
+EXPORT_SYMBOL_GPL(hdac_get_device_id);
+
+static int hdac_codec_match(struct hdac_device *dev, const struct hdac_driver *drv)
+{
+       if (hdac_get_device_id(dev, drv))
+               return 1;
+       else
+               return 0;
+}
+
+static int hda_bus_match(struct device *dev, const struct device_driver *drv)
+{
+       struct hdac_device *hdev = dev_to_hdac_dev(dev);
+       const struct hdac_driver *hdrv = drv_to_hdac_driver(drv);
+
+       if (hdev->type != hdrv->type)
+               return 0;
+
+       /*
+        * if driver provided a match function use that otherwise we will
+        * use hdac_codec_match function
+        */
+       if (hdrv->match)
+               return hdrv->match(hdev, hdrv);
+       else
+               return hdac_codec_match(hdev, hdrv);
+       return 1;
+}
+
+static int hda_uevent(const struct device *dev, struct kobj_uevent_env *env)
+{
+       char modalias[32];
+
+       snd_hdac_codec_modalias(dev_to_hdac_dev(dev), modalias,
+                               sizeof(modalias));
+       if (add_uevent_var(env, "MODALIAS=%s", modalias))
+               return -ENOMEM;
+       return 0;
+}
+
+const struct bus_type snd_hda_bus_type = {
+       .name = "hdaudio",
+       .match = hda_bus_match,
+       .uevent = hda_uevent,
+};
+EXPORT_SYMBOL_GPL(snd_hda_bus_type);
+
+static int __init hda_bus_init(void)
+{
+       return bus_register(&snd_hda_bus_type);
+}
+
+static void __exit hda_bus_exit(void)
+{
+       bus_unregister(&snd_hda_bus_type);
+}
+
+subsys_initcall(hda_bus_init);
+module_exit(hda_bus_exit);
diff --git a/sound/hda/core/hdmi_chmap.c b/sound/hda/core/hdmi_chmap.c
new file mode 100644 (file)
index 0000000..7b27604
--- /dev/null
@@ -0,0 +1,868 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * HDMI Channel map support helpers
+ */
+
+#include <linux/module.h>
+#include <sound/control.h>
+#include <sound/tlv.h>
+#include <sound/hda_chmap.h>
+
+/*
+ * CEA speaker placement:
+ *
+ *        FLH       FCH        FRH
+ *  FLW    FL  FLC   FC   FRC   FR   FRW
+ *
+ *                                  LFE
+ *                     TC
+ *
+ *          RL  RLC   RC   RRC   RR
+ *
+ * The Left/Right Surround channel _notions_ LS/RS in SMPTE 320M corresponds to
+ * CEA RL/RR; The SMPTE channel _assignment_ C/LFE is swapped to CEA LFE/FC.
+ */
+enum cea_speaker_placement {
+       FL  = (1 <<  0),        /* Front Left           */
+       FC  = (1 <<  1),        /* Front Center         */
+       FR  = (1 <<  2),        /* Front Right          */
+       FLC = (1 <<  3),        /* Front Left Center    */
+       FRC = (1 <<  4),        /* Front Right Center   */
+       RL  = (1 <<  5),        /* Rear Left            */
+       RC  = (1 <<  6),        /* Rear Center          */
+       RR  = (1 <<  7),        /* Rear Right           */
+       RLC = (1 <<  8),        /* Rear Left Center     */
+       RRC = (1 <<  9),        /* Rear Right Center    */
+       LFE = (1 << 10),        /* Low Frequency Effect */
+       FLW = (1 << 11),        /* Front Left Wide      */
+       FRW = (1 << 12),        /* Front Right Wide     */
+       FLH = (1 << 13),        /* Front Left High      */
+       FCH = (1 << 14),        /* Front Center High    */
+       FRH = (1 << 15),        /* Front Right High     */
+       TC  = (1 << 16),        /* Top Center           */
+};
+
+static const char * const cea_speaker_allocation_names[] = {
+       /*  0 */ "FL/FR",
+       /*  1 */ "LFE",
+       /*  2 */ "FC",
+       /*  3 */ "RL/RR",
+       /*  4 */ "RC",
+       /*  5 */ "FLC/FRC",
+       /*  6 */ "RLC/RRC",
+       /*  7 */ "FLW/FRW",
+       /*  8 */ "FLH/FRH",
+       /*  9 */ "TC",
+       /* 10 */ "FCH",
+};
+
+/*
+ * ELD SA bits in the CEA Speaker Allocation data block
+ */
+static const int eld_speaker_allocation_bits[] = {
+       [0] = FL | FR,
+       [1] = LFE,
+       [2] = FC,
+       [3] = RL | RR,
+       [4] = RC,
+       [5] = FLC | FRC,
+       [6] = RLC | RRC,
+       /* the following are not defined in ELD yet */
+       [7] = FLW | FRW,
+       [8] = FLH | FRH,
+       [9] = TC,
+       [10] = FCH,
+};
+
+/*
+ * ALSA sequence is:
+ *
+ *       surround40   surround41   surround50   surround51   surround71
+ * ch0   front left   =            =            =            =
+ * ch1   front right  =            =            =            =
+ * ch2   rear left    =            =            =            =
+ * ch3   rear right   =            =            =            =
+ * ch4                LFE          center       center       center
+ * ch5                                          LFE          LFE
+ * ch6                                                       side left
+ * ch7                                                       side right
+ *
+ * surround71 = {FL, FR, RLC, RRC, FC, LFE, RL, RR}
+ */
+static int hdmi_channel_mapping[0x32][8] = {
+       /* stereo */
+       [0x00] = { 0x00, 0x11, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7 },
+       /* 2.1 */
+       [0x01] = { 0x00, 0x11, 0x22, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7 },
+       /* Dolby Surround */
+       [0x02] = { 0x00, 0x11, 0x23, 0xf2, 0xf4, 0xf5, 0xf6, 0xf7 },
+       /* surround40 */
+       [0x08] = { 0x00, 0x11, 0x24, 0x35, 0xf3, 0xf2, 0xf6, 0xf7 },
+       /* 4ch */
+       [0x03] = { 0x00, 0x11, 0x23, 0x32, 0x44, 0xf5, 0xf6, 0xf7 },
+       /* surround41 */
+       [0x09] = { 0x00, 0x11, 0x24, 0x35, 0x42, 0xf3, 0xf6, 0xf7 },
+       /* surround50 */
+       [0x0a] = { 0x00, 0x11, 0x24, 0x35, 0x43, 0xf2, 0xf6, 0xf7 },
+       /* surround51 */
+       [0x0b] = { 0x00, 0x11, 0x24, 0x35, 0x43, 0x52, 0xf6, 0xf7 },
+       /* 7.1 */
+       [0x13] = { 0x00, 0x11, 0x26, 0x37, 0x43, 0x52, 0x64, 0x75 },
+};
+
+/*
+ * This is an ordered list!
+ *
+ * The preceding ones have better chances to be selected by
+ * hdmi_channel_allocation().
+ */
+static struct hdac_cea_channel_speaker_allocation channel_allocations[] = {
+/*                       channel:   7     6    5    4    3     2    1    0  */
+{ .ca_index = 0x00,  .speakers = {   0,    0,   0,   0,   0,    0,  FR,  FL } },
+                                /* 2.1 */
+{ .ca_index = 0x01,  .speakers = {   0,    0,   0,   0,   0,  LFE,  FR,  FL } },
+                                /* Dolby Surround */
+{ .ca_index = 0x02,  .speakers = {   0,    0,   0,   0,  FC,    0,  FR,  FL } },
+                                /* surround40 */
+{ .ca_index = 0x08,  .speakers = {   0,    0,  RR,  RL,   0,    0,  FR,  FL } },
+                                /* surround41 */
+{ .ca_index = 0x09,  .speakers = {   0,    0,  RR,  RL,   0,  LFE,  FR,  FL } },
+                                /* surround50 */
+{ .ca_index = 0x0a,  .speakers = {   0,    0,  RR,  RL,  FC,    0,  FR,  FL } },
+                                /* surround51 */
+{ .ca_index = 0x0b,  .speakers = {   0,    0,  RR,  RL,  FC,  LFE,  FR,  FL } },
+                                /* 6.1 */
+{ .ca_index = 0x0f,  .speakers = {   0,   RC,  RR,  RL,  FC,  LFE,  FR,  FL } },
+                                /* surround71 */
+{ .ca_index = 0x13,  .speakers = { RRC,  RLC,  RR,  RL,  FC,  LFE,  FR,  FL } },
+
+{ .ca_index = 0x03,  .speakers = {   0,    0,   0,   0,  FC,  LFE,  FR,  FL } },
+{ .ca_index = 0x04,  .speakers = {   0,    0,   0,  RC,   0,    0,  FR,  FL } },
+{ .ca_index = 0x05,  .speakers = {   0,    0,   0,  RC,   0,  LFE,  FR,  FL } },
+{ .ca_index = 0x06,  .speakers = {   0,    0,   0,  RC,  FC,    0,  FR,  FL } },
+{ .ca_index = 0x07,  .speakers = {   0,    0,   0,  RC,  FC,  LFE,  FR,  FL } },
+{ .ca_index = 0x0c,  .speakers = {   0,   RC,  RR,  RL,   0,    0,  FR,  FL } },
+{ .ca_index = 0x0d,  .speakers = {   0,   RC,  RR,  RL,   0,  LFE,  FR,  FL } },
+{ .ca_index = 0x0e,  .speakers = {   0,   RC,  RR,  RL,  FC,    0,  FR,  FL } },
+{ .ca_index = 0x10,  .speakers = { RRC,  RLC,  RR,  RL,   0,    0,  FR,  FL } },
+{ .ca_index = 0x11,  .speakers = { RRC,  RLC,  RR,  RL,   0,  LFE,  FR,  FL } },
+{ .ca_index = 0x12,  .speakers = { RRC,  RLC,  RR,  RL,  FC,    0,  FR,  FL } },
+{ .ca_index = 0x14,  .speakers = { FRC,  FLC,   0,   0,   0,    0,  FR,  FL } },
+{ .ca_index = 0x15,  .speakers = { FRC,  FLC,   0,   0,   0,  LFE,  FR,  FL } },
+{ .ca_index = 0x16,  .speakers = { FRC,  FLC,   0,   0,  FC,    0,  FR,  FL } },
+{ .ca_index = 0x17,  .speakers = { FRC,  FLC,   0,   0,  FC,  LFE,  FR,  FL } },
+{ .ca_index = 0x18,  .speakers = { FRC,  FLC,   0,  RC,   0,    0,  FR,  FL } },
+{ .ca_index = 0x19,  .speakers = { FRC,  FLC,   0,  RC,   0,  LFE,  FR,  FL } },
+{ .ca_index = 0x1a,  .speakers = { FRC,  FLC,   0,  RC,  FC,    0,  FR,  FL } },
+{ .ca_index = 0x1b,  .speakers = { FRC,  FLC,   0,  RC,  FC,  LFE,  FR,  FL } },
+{ .ca_index = 0x1c,  .speakers = { FRC,  FLC,  RR,  RL,   0,    0,  FR,  FL } },
+{ .ca_index = 0x1d,  .speakers = { FRC,  FLC,  RR,  RL,   0,  LFE,  FR,  FL } },
+{ .ca_index = 0x1e,  .speakers = { FRC,  FLC,  RR,  RL,  FC,    0,  FR,  FL } },
+{ .ca_index = 0x1f,  .speakers = { FRC,  FLC,  RR,  RL,  FC,  LFE,  FR,  FL } },
+{ .ca_index = 0x20,  .speakers = {   0,  FCH,  RR,  RL,  FC,    0,  FR,  FL } },
+{ .ca_index = 0x21,  .speakers = {   0,  FCH,  RR,  RL,  FC,  LFE,  FR,  FL } },
+{ .ca_index = 0x22,  .speakers = {  TC,    0,  RR,  RL,  FC,    0,  FR,  FL } },
+{ .ca_index = 0x23,  .speakers = {  TC,    0,  RR,  RL,  FC,  LFE,  FR,  FL } },
+{ .ca_index = 0x24,  .speakers = { FRH,  FLH,  RR,  RL,   0,    0,  FR,  FL } },
+{ .ca_index = 0x25,  .speakers = { FRH,  FLH,  RR,  RL,   0,  LFE,  FR,  FL } },
+{ .ca_index = 0x26,  .speakers = { FRW,  FLW,  RR,  RL,   0,    0,  FR,  FL } },
+{ .ca_index = 0x27,  .speakers = { FRW,  FLW,  RR,  RL,   0,  LFE,  FR,  FL } },
+{ .ca_index = 0x28,  .speakers = {  TC,   RC,  RR,  RL,  FC,    0,  FR,  FL } },
+{ .ca_index = 0x29,  .speakers = {  TC,   RC,  RR,  RL,  FC,  LFE,  FR,  FL } },
+{ .ca_index = 0x2a,  .speakers = { FCH,   RC,  RR,  RL,  FC,    0,  FR,  FL } },
+{ .ca_index = 0x2b,  .speakers = { FCH,   RC,  RR,  RL,  FC,  LFE,  FR,  FL } },
+{ .ca_index = 0x2c,  .speakers = {  TC,  FCH,  RR,  RL,  FC,    0,  FR,  FL } },
+{ .ca_index = 0x2d,  .speakers = {  TC,  FCH,  RR,  RL,  FC,  LFE,  FR,  FL } },
+{ .ca_index = 0x2e,  .speakers = { FRH,  FLH,  RR,  RL,  FC,    0,  FR,  FL } },
+{ .ca_index = 0x2f,  .speakers = { FRH,  FLH,  RR,  RL,  FC,  LFE,  FR,  FL } },
+{ .ca_index = 0x30,  .speakers = { FRW,  FLW,  RR,  RL,  FC,    0,  FR,  FL } },
+{ .ca_index = 0x31,  .speakers = { FRW,  FLW,  RR,  RL,  FC,  LFE,  FR,  FL } },
+};
+
+static int hdmi_pin_set_slot_channel(struct hdac_device *codec,
+               hda_nid_t pin_nid, int asp_slot, int channel)
+{
+       return snd_hdac_codec_write(codec, pin_nid, 0,
+                               AC_VERB_SET_HDMI_CHAN_SLOT,
+                               (channel << 4) | asp_slot);
+}
+
+static int hdmi_pin_get_slot_channel(struct hdac_device *codec,
+                       hda_nid_t pin_nid, int asp_slot)
+{
+       return (snd_hdac_codec_read(codec, pin_nid, 0,
+                                  AC_VERB_GET_HDMI_CHAN_SLOT,
+                                  asp_slot) & 0xf0) >> 4;
+}
+
+static int hdmi_get_channel_count(struct hdac_device *codec, hda_nid_t cvt_nid)
+{
+       return 1 + snd_hdac_codec_read(codec, cvt_nid, 0,
+                                       AC_VERB_GET_CVT_CHAN_COUNT, 0);
+}
+
+static void hdmi_set_channel_count(struct hdac_device *codec,
+                                  hda_nid_t cvt_nid, int chs)
+{
+       if (chs != hdmi_get_channel_count(codec, cvt_nid))
+               snd_hdac_codec_write(codec, cvt_nid, 0,
+                                   AC_VERB_SET_CVT_CHAN_COUNT, chs - 1);
+}
+
+/*
+ * Channel mapping routines
+ */
+
+/*
+ * Compute derived values in channel_allocations[].
+ */
+static void init_channel_allocations(void)
+{
+       int i, j;
+       struct hdac_cea_channel_speaker_allocation *p;
+
+       for (i = 0; i < ARRAY_SIZE(channel_allocations); i++) {
+               p = channel_allocations + i;
+               p->channels = 0;
+               p->spk_mask = 0;
+               for (j = 0; j < ARRAY_SIZE(p->speakers); j++)
+                       if (p->speakers[j]) {
+                               p->channels++;
+                               p->spk_mask |= p->speakers[j];
+                       }
+       }
+}
+
+static int get_channel_allocation_order(int ca)
+{
+       int i;
+
+       for (i = 0; i < ARRAY_SIZE(channel_allocations); i++) {
+               if (channel_allocations[i].ca_index == ca)
+                       break;
+       }
+       return i;
+}
+
+void snd_hdac_print_channel_allocation(int spk_alloc, char *buf, int buflen)
+{
+       int i, j;
+
+       for (i = 0, j = 0; i < ARRAY_SIZE(cea_speaker_allocation_names); i++) {
+               if (spk_alloc & (1 << i))
+                       j += scnprintf(buf + j, buflen - j,  " %s",
+                                       cea_speaker_allocation_names[i]);
+       }
+       buf[j] = '\0';  /* necessary when j == 0 */
+}
+EXPORT_SYMBOL_GPL(snd_hdac_print_channel_allocation);
+
+/*
+ * The transformation takes two steps:
+ *
+ *     eld->spk_alloc => (eld_speaker_allocation_bits[]) => spk_mask
+ *           spk_mask => (channel_allocations[])         => ai->CA
+ *
+ * TODO: it could select the wrong CA from multiple candidates.
+*/
+static int hdmi_channel_allocation_spk_alloc_blk(struct hdac_device *codec,
+                                  int spk_alloc, int channels)
+{
+       int i;
+       int ca = 0;
+       int spk_mask = 0;
+       char buf[SND_PRINT_CHANNEL_ALLOCATION_ADVISED_BUFSIZE];
+
+       /*
+        * CA defaults to 0 for basic stereo audio
+        */
+       if (channels <= 2)
+               return 0;
+
+       /*
+        * expand ELD's speaker allocation mask
+        *
+        * ELD tells the speaker mask in a compact(paired) form,
+        * expand ELD's notions to match the ones used by Audio InfoFrame.
+        */
+       for (i = 0; i < ARRAY_SIZE(eld_speaker_allocation_bits); i++) {
+               if (spk_alloc & (1 << i))
+                       spk_mask |= eld_speaker_allocation_bits[i];
+       }
+
+       /* search for the first working match in the CA table */
+       for (i = 0; i < ARRAY_SIZE(channel_allocations); i++) {
+               if (channels == channel_allocations[i].channels &&
+                   (spk_mask & channel_allocations[i].spk_mask) ==
+                               channel_allocations[i].spk_mask) {
+                       ca = channel_allocations[i].ca_index;
+                       break;
+               }
+       }
+
+       if (!ca) {
+               /*
+                * if there was no match, select the regular ALSA channel
+                * allocation with the matching number of channels
+                */
+               for (i = 0; i < ARRAY_SIZE(channel_allocations); i++) {
+                       if (channels == channel_allocations[i].channels) {
+                               ca = channel_allocations[i].ca_index;
+                               break;
+                       }
+               }
+       }
+
+       snd_hdac_print_channel_allocation(spk_alloc, buf, sizeof(buf));
+       dev_dbg(&codec->dev, "HDMI: select CA 0x%x for %d-channel allocation: %s\n",
+                   ca, channels, buf);
+
+       return ca;
+}
+
+static void hdmi_debug_channel_mapping(struct hdac_chmap *chmap,
+                                      hda_nid_t pin_nid)
+{
+#ifdef CONFIG_SND_DEBUG_VERBOSE
+       int i;
+       int channel;
+
+       for (i = 0; i < 8; i++) {
+               channel = chmap->ops.pin_get_slot_channel(
+                               chmap->hdac, pin_nid, i);
+               dev_dbg(&chmap->hdac->dev, "HDMI: ASP channel %d => slot %d\n",
+                                               channel, i);
+       }
+#endif
+}
+
+static void hdmi_std_setup_channel_mapping(struct hdac_chmap *chmap,
+                                      hda_nid_t pin_nid,
+                                      bool non_pcm,
+                                      int ca)
+{
+       struct hdac_cea_channel_speaker_allocation *ch_alloc;
+       int i;
+       int err;
+       int order;
+       int non_pcm_mapping[8];
+
+       order = get_channel_allocation_order(ca);
+       ch_alloc = &channel_allocations[order];
+
+       if (hdmi_channel_mapping[ca][1] == 0) {
+               int hdmi_slot = 0;
+               /* fill actual channel mappings in ALSA channel (i) order */
+               for (i = 0; i < ch_alloc->channels; i++) {
+                       while (!WARN_ON(hdmi_slot >= 8) &&
+                              !ch_alloc->speakers[7 - hdmi_slot])
+                               hdmi_slot++; /* skip zero slots */
+
+                       hdmi_channel_mapping[ca][i] = (i << 4) | hdmi_slot++;
+               }
+               /* fill the rest of the slots with ALSA channel 0xf */
+               for (hdmi_slot = 0; hdmi_slot < 8; hdmi_slot++)
+                       if (!ch_alloc->speakers[7 - hdmi_slot])
+                               hdmi_channel_mapping[ca][i++] = (0xf << 4) | hdmi_slot;
+       }
+
+       if (non_pcm) {
+               for (i = 0; i < ch_alloc->channels; i++)
+                       non_pcm_mapping[i] = (i << 4) | i;
+               for (; i < 8; i++)
+                       non_pcm_mapping[i] = (0xf << 4) | i;
+       }
+
+       for (i = 0; i < 8; i++) {
+               int slotsetup = non_pcm ? non_pcm_mapping[i] : hdmi_channel_mapping[ca][i];
+               int hdmi_slot = slotsetup & 0x0f;
+               int channel = (slotsetup & 0xf0) >> 4;
+
+               err = chmap->ops.pin_set_slot_channel(chmap->hdac,
+                               pin_nid, hdmi_slot, channel);
+               if (err) {
+                       dev_dbg(&chmap->hdac->dev, "HDMI: channel mapping failed\n");
+                       break;
+               }
+       }
+}
+
+struct channel_map_table {
+       unsigned char map;              /* ALSA API channel map position */
+       int spk_mask;                   /* speaker position bit mask */
+};
+
+static struct channel_map_table map_tables[] = {
+       { SNDRV_CHMAP_FL,       FL },
+       { SNDRV_CHMAP_FR,       FR },
+       { SNDRV_CHMAP_RL,       RL },
+       { SNDRV_CHMAP_RR,       RR },
+       { SNDRV_CHMAP_LFE,      LFE },
+       { SNDRV_CHMAP_FC,       FC },
+       { SNDRV_CHMAP_RLC,      RLC },
+       { SNDRV_CHMAP_RRC,      RRC },
+       { SNDRV_CHMAP_RC,       RC },
+       { SNDRV_CHMAP_FLC,      FLC },
+       { SNDRV_CHMAP_FRC,      FRC },
+       { SNDRV_CHMAP_TFL,      FLH },
+       { SNDRV_CHMAP_TFR,      FRH },
+       { SNDRV_CHMAP_FLW,      FLW },
+       { SNDRV_CHMAP_FRW,      FRW },
+       { SNDRV_CHMAP_TC,       TC },
+       { SNDRV_CHMAP_TFC,      FCH },
+       {} /* terminator */
+};
+
+/* from ALSA API channel position to speaker bit mask */
+int snd_hdac_chmap_to_spk_mask(unsigned char c)
+{
+       struct channel_map_table *t = map_tables;
+
+       for (; t->map; t++) {
+               if (t->map == c)
+                       return t->spk_mask;
+       }
+       return 0;
+}
+EXPORT_SYMBOL_GPL(snd_hdac_chmap_to_spk_mask);
+
+/* from ALSA API channel position to CEA slot */
+static int to_cea_slot(int ordered_ca, unsigned char pos)
+{
+       int mask = snd_hdac_chmap_to_spk_mask(pos);
+       int i;
+
+       /* Add sanity check to pass klockwork check.
+        * This should never happen.
+        */
+       if (ordered_ca >= ARRAY_SIZE(channel_allocations))
+               return -1;
+
+       if (mask) {
+               for (i = 0; i < 8; i++) {
+                       if (channel_allocations[ordered_ca].speakers[7 - i] == mask)
+                               return i;
+               }
+       }
+
+       return -1;
+}
+
+/* from speaker bit mask to ALSA API channel position */
+int snd_hdac_spk_to_chmap(int spk)
+{
+       struct channel_map_table *t = map_tables;
+
+       for (; t->map; t++) {
+               if (t->spk_mask == spk)
+                       return t->map;
+       }
+       return 0;
+}
+EXPORT_SYMBOL_GPL(snd_hdac_spk_to_chmap);
+
+/* from CEA slot to ALSA API channel position */
+static int from_cea_slot(int ordered_ca, unsigned char slot)
+{
+       int mask;
+
+       /* Add sanity check to pass klockwork check.
+        * This should never happen.
+        */
+       if (slot >= 8)
+               return 0;
+
+       mask = channel_allocations[ordered_ca].speakers[7 - slot];
+
+       return snd_hdac_spk_to_chmap(mask);
+}
+
+/* get the CA index corresponding to the given ALSA API channel map */
+static int hdmi_manual_channel_allocation(int chs, unsigned char *map)
+{
+       int i, spks = 0, spk_mask = 0;
+
+       for (i = 0; i < chs; i++) {
+               int mask = snd_hdac_chmap_to_spk_mask(map[i]);
+
+               if (mask) {
+                       spk_mask |= mask;
+                       spks++;
+               }
+       }
+
+       for (i = 0; i < ARRAY_SIZE(channel_allocations); i++) {
+               if ((chs == channel_allocations[i].channels ||
+                    spks == channel_allocations[i].channels) &&
+                   (spk_mask & channel_allocations[i].spk_mask) ==
+                               channel_allocations[i].spk_mask)
+                       return channel_allocations[i].ca_index;
+       }
+       return -1;
+}
+
+/* set up the channel slots for the given ALSA API channel map */
+static int hdmi_manual_setup_channel_mapping(struct hdac_chmap *chmap,
+                                            hda_nid_t pin_nid,
+                                            int chs, unsigned char *map,
+                                            int ca)
+{
+       int ordered_ca = get_channel_allocation_order(ca);
+       int alsa_pos, hdmi_slot;
+       int assignments[8] = {[0 ... 7] = 0xf};
+
+       for (alsa_pos = 0; alsa_pos < chs; alsa_pos++) {
+
+               hdmi_slot = to_cea_slot(ordered_ca, map[alsa_pos]);
+
+               if (hdmi_slot < 0)
+                       continue; /* unassigned channel */
+
+               assignments[hdmi_slot] = alsa_pos;
+       }
+
+       for (hdmi_slot = 0; hdmi_slot < 8; hdmi_slot++) {
+               int err;
+
+               err = chmap->ops.pin_set_slot_channel(chmap->hdac,
+                               pin_nid, hdmi_slot, assignments[hdmi_slot]);
+               if (err)
+                       return -EINVAL;
+       }
+       return 0;
+}
+
+/* store ALSA API channel map from the current default map */
+static void hdmi_setup_fake_chmap(unsigned char *map, int ca)
+{
+       int i;
+       int ordered_ca = get_channel_allocation_order(ca);
+
+       for (i = 0; i < 8; i++) {
+               if (ordered_ca < ARRAY_SIZE(channel_allocations) &&
+                   i < channel_allocations[ordered_ca].channels)
+                       map[i] = from_cea_slot(ordered_ca, hdmi_channel_mapping[ca][i] & 0x0f);
+               else
+                       map[i] = 0;
+       }
+}
+
+void snd_hdac_setup_channel_mapping(struct hdac_chmap *chmap,
+                                      hda_nid_t pin_nid, bool non_pcm, int ca,
+                                      int channels, unsigned char *map,
+                                      bool chmap_set)
+{
+       if (!non_pcm && chmap_set) {
+               hdmi_manual_setup_channel_mapping(chmap, pin_nid,
+                                                 channels, map, ca);
+       } else {
+               hdmi_std_setup_channel_mapping(chmap, pin_nid, non_pcm, ca);
+               hdmi_setup_fake_chmap(map, ca);
+       }
+
+       hdmi_debug_channel_mapping(chmap, pin_nid);
+}
+EXPORT_SYMBOL_GPL(snd_hdac_setup_channel_mapping);
+
+int snd_hdac_get_active_channels(int ca)
+{
+       int ordered_ca = get_channel_allocation_order(ca);
+
+       /* Add sanity check to pass klockwork check.
+        * This should never happen.
+        */
+       if (ordered_ca >= ARRAY_SIZE(channel_allocations))
+               ordered_ca = 0;
+
+       return channel_allocations[ordered_ca].channels;
+}
+EXPORT_SYMBOL_GPL(snd_hdac_get_active_channels);
+
+struct hdac_cea_channel_speaker_allocation *snd_hdac_get_ch_alloc_from_ca(int ca)
+{
+       return &channel_allocations[get_channel_allocation_order(ca)];
+}
+EXPORT_SYMBOL_GPL(snd_hdac_get_ch_alloc_from_ca);
+
+int snd_hdac_channel_allocation(struct hdac_device *hdac, int spk_alloc,
+               int channels, bool chmap_set, bool non_pcm, unsigned char *map)
+{
+       int ca;
+
+       if (!non_pcm && chmap_set)
+               ca = hdmi_manual_channel_allocation(channels, map);
+       else
+               ca = hdmi_channel_allocation_spk_alloc_blk(hdac,
+                                       spk_alloc, channels);
+
+       if (ca < 0)
+               ca = 0;
+
+       return ca;
+}
+EXPORT_SYMBOL_GPL(snd_hdac_channel_allocation);
+
+/*
+ * ALSA API channel-map control callbacks
+ */
+static int hdmi_chmap_ctl_info(struct snd_kcontrol *kcontrol,
+                              struct snd_ctl_elem_info *uinfo)
+{
+       struct snd_pcm_chmap *info = snd_kcontrol_chip(kcontrol);
+       struct hdac_chmap *chmap = info->private_data;
+
+       uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+       uinfo->count = chmap->channels_max;
+       uinfo->value.integer.min = 0;
+       uinfo->value.integer.max = SNDRV_CHMAP_LAST;
+       return 0;
+}
+
+static int hdmi_chmap_cea_alloc_validate_get_type(struct hdac_chmap *chmap,
+               struct hdac_cea_channel_speaker_allocation *cap, int channels)
+{
+       /* If the speaker allocation matches the channel count, it is OK.*/
+       if (cap->channels != channels)
+               return -1;
+
+       /* all channels are remappable freely */
+       return SNDRV_CTL_TLVT_CHMAP_VAR;
+}
+
+static void hdmi_cea_alloc_to_tlv_chmap(struct hdac_chmap *hchmap,
+               struct hdac_cea_channel_speaker_allocation *cap,
+               unsigned int *chmap, int channels)
+{
+       int count = 0;
+       int c;
+
+       for (c = 7; c >= 0; c--) {
+               int spk = cap->speakers[c];
+
+               if (!spk)
+                       continue;
+
+               chmap[count++] = snd_hdac_spk_to_chmap(spk);
+       }
+
+       WARN_ON(count != channels);
+}
+
+static int spk_mask_from_spk_alloc(int spk_alloc)
+{
+       int i;
+       int spk_mask = eld_speaker_allocation_bits[0];
+
+       for (i = 0; i < ARRAY_SIZE(eld_speaker_allocation_bits); i++) {
+               if (spk_alloc & (1 << i))
+                       spk_mask |= eld_speaker_allocation_bits[i];
+       }
+
+       return spk_mask;
+}
+
+static int hdmi_chmap_ctl_tlv(struct snd_kcontrol *kcontrol, int op_flag,
+                             unsigned int size, unsigned int __user *tlv)
+{
+       struct snd_pcm_chmap *info = snd_kcontrol_chip(kcontrol);
+       struct hdac_chmap *chmap = info->private_data;
+       int pcm_idx = kcontrol->private_value;
+       unsigned int __user *dst;
+       int chs, count = 0;
+       unsigned long max_chs;
+       int type;
+       int spk_alloc, spk_mask;
+
+       if (size < 8)
+               return -ENOMEM;
+       if (put_user(SNDRV_CTL_TLVT_CONTAINER, tlv))
+               return -EFAULT;
+       size -= 8;
+       dst = tlv + 2;
+
+       spk_alloc = chmap->ops.get_spk_alloc(chmap->hdac, pcm_idx);
+       spk_mask = spk_mask_from_spk_alloc(spk_alloc);
+
+       max_chs = hweight_long(spk_mask);
+
+       for (chs = 2; chs <= max_chs; chs++) {
+               int i;
+               struct hdac_cea_channel_speaker_allocation *cap;
+
+               cap = channel_allocations;
+               for (i = 0; i < ARRAY_SIZE(channel_allocations); i++, cap++) {
+                       int chs_bytes = chs * 4;
+                       unsigned int tlv_chmap[8];
+
+                       if (cap->channels != chs)
+                               continue;
+
+                       if (!(cap->spk_mask == (spk_mask & cap->spk_mask)))
+                               continue;
+
+                       type = chmap->ops.chmap_cea_alloc_validate_get_type(
+                                                       chmap, cap, chs);
+                       if (type < 0)
+                               return -ENODEV;
+                       if (size < 8)
+                               return -ENOMEM;
+
+                       if (put_user(type, dst) ||
+                           put_user(chs_bytes, dst + 1))
+                               return -EFAULT;
+
+                       dst += 2;
+                       size -= 8;
+                       count += 8;
+
+                       if (size < chs_bytes)
+                               return -ENOMEM;
+
+                       size -= chs_bytes;
+                       count += chs_bytes;
+                       chmap->ops.cea_alloc_to_tlv_chmap(chmap, cap,
+                                               tlv_chmap, chs);
+
+                       if (copy_to_user(dst, tlv_chmap, chs_bytes))
+                               return -EFAULT;
+                       dst += chs;
+               }
+       }
+
+       if (put_user(count, tlv + 1))
+               return -EFAULT;
+
+       return 0;
+}
+
+static int hdmi_chmap_ctl_get(struct snd_kcontrol *kcontrol,
+                             struct snd_ctl_elem_value *ucontrol)
+{
+       struct snd_pcm_chmap *info = snd_kcontrol_chip(kcontrol);
+       struct hdac_chmap *chmap = info->private_data;
+       int pcm_idx = kcontrol->private_value;
+       unsigned char pcm_chmap[8];
+       int i;
+
+       memset(pcm_chmap, 0, sizeof(pcm_chmap));
+       chmap->ops.get_chmap(chmap->hdac, pcm_idx, pcm_chmap);
+
+       for (i = 0; i < ARRAY_SIZE(pcm_chmap); i++)
+               ucontrol->value.integer.value[i] = pcm_chmap[i];
+
+       return 0;
+}
+
+/* a simple sanity check for input values to chmap kcontrol */
+static int chmap_value_check(struct hdac_chmap *hchmap,
+                            const struct snd_ctl_elem_value *ucontrol)
+{
+       int i;
+
+       for (i = 0; i < hchmap->channels_max; i++) {
+               if (ucontrol->value.integer.value[i] < 0 ||
+                   ucontrol->value.integer.value[i] > SNDRV_CHMAP_LAST)
+                       return -EINVAL;
+       }
+       return 0;
+}
+
+static int hdmi_chmap_ctl_put(struct snd_kcontrol *kcontrol,
+                             struct snd_ctl_elem_value *ucontrol)
+{
+       struct snd_pcm_chmap *info = snd_kcontrol_chip(kcontrol);
+       struct hdac_chmap *hchmap = info->private_data;
+       int pcm_idx = kcontrol->private_value;
+       unsigned int ctl_idx;
+       struct snd_pcm_substream *substream;
+       unsigned char chmap[8], per_pin_chmap[8];
+       int i, err, ca, prepared = 0;
+
+       err = chmap_value_check(hchmap, ucontrol);
+       if (err < 0)
+               return err;
+
+       /* No monitor is connected in dyn_pcm_assign.
+        * It's invalid to setup the chmap
+        */
+       if (!hchmap->ops.is_pcm_attached(hchmap->hdac, pcm_idx))
+               return 0;
+
+       ctl_idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
+       substream = snd_pcm_chmap_substream(info, ctl_idx);
+       if (!substream || !substream->runtime)
+               return 0; /* just for avoiding error from alsactl restore */
+       switch (substream->runtime->state) {
+       case SNDRV_PCM_STATE_OPEN:
+       case SNDRV_PCM_STATE_SETUP:
+               break;
+       case SNDRV_PCM_STATE_PREPARED:
+               prepared = 1;
+               break;
+       default:
+               return -EBUSY;
+       }
+       memset(chmap, 0, sizeof(chmap));
+       for (i = 0; i < ARRAY_SIZE(chmap); i++)
+               chmap[i] = ucontrol->value.integer.value[i];
+
+       hchmap->ops.get_chmap(hchmap->hdac, pcm_idx, per_pin_chmap);
+       if (!memcmp(chmap, per_pin_chmap, sizeof(chmap)))
+               return 0;
+       ca = hdmi_manual_channel_allocation(ARRAY_SIZE(chmap), chmap);
+       if (ca < 0)
+               return -EINVAL;
+       if (hchmap->ops.chmap_validate) {
+               err = hchmap->ops.chmap_validate(hchmap, ca,
+                               ARRAY_SIZE(chmap), chmap);
+               if (err)
+                       return err;
+       }
+
+       hchmap->ops.set_chmap(hchmap->hdac, pcm_idx, chmap, prepared);
+
+       return 0;
+}
+
+static const struct hdac_chmap_ops chmap_ops = {
+       .chmap_cea_alloc_validate_get_type      = hdmi_chmap_cea_alloc_validate_get_type,
+       .cea_alloc_to_tlv_chmap                 = hdmi_cea_alloc_to_tlv_chmap,
+       .pin_get_slot_channel                   = hdmi_pin_get_slot_channel,
+       .pin_set_slot_channel                   = hdmi_pin_set_slot_channel,
+       .set_channel_count                      = hdmi_set_channel_count,
+};
+
+void snd_hdac_register_chmap_ops(struct hdac_device *hdac,
+                               struct hdac_chmap *chmap)
+{
+       chmap->ops = chmap_ops;
+       chmap->hdac = hdac;
+       init_channel_allocations();
+}
+EXPORT_SYMBOL_GPL(snd_hdac_register_chmap_ops);
+
+int snd_hdac_add_chmap_ctls(struct snd_pcm *pcm, int pcm_idx,
+                               struct hdac_chmap *hchmap)
+{
+       struct snd_pcm_chmap *chmap;
+       struct snd_kcontrol *kctl;
+       int err, i;
+
+       err = snd_pcm_add_chmap_ctls(pcm,
+                                    SNDRV_PCM_STREAM_PLAYBACK,
+                                    NULL, 0, pcm_idx, &chmap);
+       if (err < 0)
+               return err;
+       /* override handlers */
+       chmap->private_data = hchmap;
+       kctl = chmap->kctl;
+       for (i = 0; i < kctl->count; i++)
+               kctl->vd[i].access |= SNDRV_CTL_ELEM_ACCESS_WRITE;
+       kctl->info = hdmi_chmap_ctl_info;
+       kctl->get = hdmi_chmap_ctl_get;
+       kctl->put = hdmi_chmap_ctl_put;
+       kctl->tlv.c = hdmi_chmap_ctl_tlv;
+
+       return 0;
+}
+EXPORT_SYMBOL_GPL(snd_hdac_add_chmap_ctls);
diff --git a/sound/hda/core/i915.c b/sound/hda/core/i915.c
new file mode 100644 (file)
index 0000000..e942521
--- /dev/null
@@ -0,0 +1,208 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ *  hdac_i915.c - routines for sync between HD-A core and i915 display driver
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/pci.h>
+#include <sound/core.h>
+#include <sound/hdaudio.h>
+#include <sound/hda_i915.h>
+#include <sound/hda_register.h>
+#include <video/nomodeset.h>
+
+static int gpu_bind = -1;
+module_param(gpu_bind, int, 0644);
+MODULE_PARM_DESC(gpu_bind, "Whether to bind sound component to GPU "
+                          "(1=always, 0=never, -1=on nomodeset(default))");
+
+/**
+ * snd_hdac_i915_set_bclk - Reprogram BCLK for HSW/BDW
+ * @bus: HDA core bus
+ *
+ * Intel HSW/BDW display HDA controller is in GPU. Both its power and link BCLK
+ * depends on GPU. Two Extended Mode registers EM4 (M value) and EM5 (N Value)
+ * are used to convert CDClk (Core Display Clock) to 24MHz BCLK:
+ * BCLK = CDCLK * M / N
+ * The values will be lost when the display power well is disabled and need to
+ * be restored to avoid abnormal playback speed.
+ *
+ * Call this function at initializing and changing power well, as well as
+ * at ELD notifier for the hotplug.
+ */
+void snd_hdac_i915_set_bclk(struct hdac_bus *bus)
+{
+       struct drm_audio_component *acomp = bus->audio_component;
+       struct pci_dev *pci = to_pci_dev(bus->dev);
+       int cdclk_freq;
+       unsigned int bclk_m, bclk_n;
+
+       if (!acomp || !acomp->ops || !acomp->ops->get_cdclk_freq)
+               return; /* only for i915 binding */
+       if (!HDA_CONTROLLER_IS_HSW(pci))
+               return; /* only HSW/BDW */
+
+       cdclk_freq = acomp->ops->get_cdclk_freq(acomp->dev);
+       switch (cdclk_freq) {
+       case 337500:
+               bclk_m = 16;
+               bclk_n = 225;
+               break;
+
+       case 450000:
+       default: /* default CDCLK 450MHz */
+               bclk_m = 4;
+               bclk_n = 75;
+               break;
+
+       case 540000:
+               bclk_m = 4;
+               bclk_n = 90;
+               break;
+
+       case 675000:
+               bclk_m = 8;
+               bclk_n = 225;
+               break;
+       }
+
+       snd_hdac_chip_writew(bus, HSW_EM4, bclk_m);
+       snd_hdac_chip_writew(bus, HSW_EM5, bclk_n);
+}
+EXPORT_SYMBOL_GPL(snd_hdac_i915_set_bclk);
+
+/* returns true if the devices can be connected for audio */
+static bool connectivity_check(struct pci_dev *i915, struct pci_dev *hdac)
+{
+       struct pci_bus *bus_a = i915->bus, *bus_b = hdac->bus;
+
+       /* directly connected on the same bus */
+       if (bus_a == bus_b)
+               return true;
+
+       bus_a = bus_a->parent;
+       bus_b = bus_b->parent;
+
+       /* connected via parent bus (may be NULL!) */
+       if (bus_a == bus_b)
+               return true;
+
+       if (!bus_a || !bus_b)
+               return false;
+
+       /*
+        * on i915 discrete GPUs with embedded HDA audio, the two
+        * devices are connected via 2nd level PCI bridge
+        */
+       bus_a = bus_a->parent;
+       bus_b = bus_b->parent;
+       if (bus_a && bus_a == bus_b)
+               return true;
+
+       return false;
+}
+
+static int i915_component_master_match(struct device *dev, int subcomponent,
+                                      void *data)
+{
+       struct pci_dev *hdac_pci, *i915_pci;
+       struct hdac_bus *bus = data;
+
+       if (!dev_is_pci(dev))
+               return 0;
+
+       hdac_pci = to_pci_dev(bus->dev);
+       i915_pci = to_pci_dev(dev);
+
+       if ((!strcmp(dev->driver->name, "i915") ||
+                !strcmp(dev->driver->name, "xe")) &&
+           subcomponent == I915_COMPONENT_AUDIO &&
+           connectivity_check(i915_pci, hdac_pci))
+               return 1;
+
+       return 0;
+}
+
+/* check whether Intel graphics is present and reachable */
+static int i915_gfx_present(struct pci_dev *hdac_pci)
+{
+       /* List of known platforms with no i915 support. */
+       static const struct pci_device_id denylist[] = {
+               /* CNL */
+               { PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x5a40), 0x030000, 0xff0000 },
+               { PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x5a41), 0x030000, 0xff0000 },
+               { PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x5a42), 0x030000, 0xff0000 },
+               { PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x5a44), 0x030000, 0xff0000 },
+               { PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x5a49), 0x030000, 0xff0000 },
+               { PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x5a4a), 0x030000, 0xff0000 },
+               { PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x5a4c), 0x030000, 0xff0000 },
+               { PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x5a50), 0x030000, 0xff0000 },
+               { PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x5a51), 0x030000, 0xff0000 },
+               { PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x5a52), 0x030000, 0xff0000 },
+               { PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x5a54), 0x030000, 0xff0000 },
+               { PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x5a59), 0x030000, 0xff0000 },
+               { PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x5a5a), 0x030000, 0xff0000 },
+               { PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x5a5c), 0x030000, 0xff0000 },
+               /* LKF */
+               { PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x9840), 0x030000, 0xff0000 },
+               {}
+       };
+       struct pci_dev *display_dev = NULL;
+
+       if (!gpu_bind || (gpu_bind < 0 && video_firmware_drivers_only()))
+               return false;
+
+       for_each_pci_dev(display_dev) {
+               if (display_dev->vendor != PCI_VENDOR_ID_INTEL ||
+                   (display_dev->class >> 16) != PCI_BASE_CLASS_DISPLAY)
+                       continue;
+
+               if (pci_match_id(denylist, display_dev))
+                       continue;
+
+               if (connectivity_check(display_dev, hdac_pci)) {
+                       pci_dev_put(display_dev);
+                       return true;
+               }
+       }
+
+       return false;
+}
+
+/**
+ * snd_hdac_i915_init - Initialize i915 audio component
+ * @bus: HDA core bus
+ *
+ * This function is supposed to be used only by a HD-audio controller
+ * driver that needs the interaction with i915 graphics.
+ *
+ * This function initializes and sets up the audio component to communicate
+ * with i915 graphics driver.
+ *
+ * Returns zero for success or a negative error code.
+ */
+int snd_hdac_i915_init(struct hdac_bus *bus)
+{
+       struct drm_audio_component *acomp;
+       int err;
+
+       if (!i915_gfx_present(to_pci_dev(bus->dev)))
+               return -ENODEV;
+
+       err = snd_hdac_acomp_init(bus, NULL,
+                                 i915_component_master_match,
+                                 sizeof(struct i915_audio_component) - sizeof(*acomp));
+       if (err < 0)
+               return err;
+       acomp = bus->audio_component;
+       if (!acomp)
+               return -ENODEV;
+       if (!acomp->ops) {
+               snd_hdac_acomp_exit(bus);
+               return dev_err_probe(bus->dev, -EPROBE_DEFER,
+                                    "couldn't bind with audio component\n");
+       }
+       return 0;
+}
+EXPORT_SYMBOL_GPL(snd_hdac_i915_init);
diff --git a/sound/hda/core/intel-dsp-config.c b/sound/hda/core/intel-dsp-config.c
new file mode 100644 (file)
index 0000000..3cb1e7f
--- /dev/null
@@ -0,0 +1,821 @@
+// SPDX-License-Identifier: GPL-2.0
+// Copyright (c) 2019 Jaroslav Kysela <perex@perex.cz>
+
+#include <linux/acpi.h>
+#include <linux/bits.h>
+#include <linux/dmi.h>
+#include <linux/module.h>
+#include <linux/pci.h>
+#include <linux/soundwire/sdw.h>
+#include <linux/soundwire/sdw_intel.h>
+#include <sound/core.h>
+#include <sound/intel-dsp-config.h>
+#include <sound/intel-nhlt.h>
+#include <sound/soc-acpi.h>
+
+#include <acpi/nhlt.h>
+
+static int dsp_driver;
+
+module_param(dsp_driver, int, 0444);
+MODULE_PARM_DESC(dsp_driver, "Force the DSP driver for Intel DSP (0=auto, 1=legacy, 2=SST, 3=SOF, 4=AVS)");
+
+#define FLAG_SST                       BIT(0)
+#define FLAG_SOF                       BIT(1)
+#define FLAG_SST_ONLY_IF_DMIC          BIT(15)
+#define FLAG_SOF_ONLY_IF_DMIC          BIT(16)
+#define FLAG_SOF_ONLY_IF_SOUNDWIRE     BIT(17)
+
+#define FLAG_SOF_ONLY_IF_DMIC_OR_SOUNDWIRE (FLAG_SOF_ONLY_IF_DMIC | \
+                                           FLAG_SOF_ONLY_IF_SOUNDWIRE)
+
+struct config_entry {
+       u32 flags;
+       u16 device;
+       u8 acpi_hid[ACPI_ID_LEN];
+       const struct dmi_system_id *dmi_table;
+       const struct snd_soc_acpi_codecs *codec_hid;
+};
+
+static const struct snd_soc_acpi_codecs __maybe_unused essx_83x6 = {
+       .num_codecs = 3,
+       .codecs = { "ESSX8316", "ESSX8326", "ESSX8336"},
+};
+
+/*
+ * configuration table
+ * - the order of similar PCI ID entries is important!
+ * - the first successful match will win
+ */
+static const struct config_entry config_table[] = {
+/* Merrifield */
+#if IS_ENABLED(CONFIG_SND_SOC_SOF_MERRIFIELD)
+       {
+               .flags = FLAG_SOF,
+               .device = PCI_DEVICE_ID_INTEL_SST_TNG,
+       },
+#endif
+/*
+ * Skylake, Kabylake, Apollolake
+ * the legacy HDAudio driver is used except on Up Squared (SOF) and
+ * Chromebooks (SST), as well as devices based on the ES8336 codec
+ */
+#if IS_ENABLED(CONFIG_SND_SOC_INTEL_AVS)
+       {
+               .flags = FLAG_SST,
+               .device = PCI_DEVICE_ID_INTEL_HDA_SKL_LP,
+               .dmi_table = (const struct dmi_system_id []) {
+                       {
+                               .ident = "Google Chromebooks",
+                               .matches = {
+                                       DMI_MATCH(DMI_SYS_VENDOR, "Google"),
+                               }
+                       },
+                       {}
+               }
+       },
+       {
+               .flags = FLAG_SST | FLAG_SST_ONLY_IF_DMIC,
+               .device = PCI_DEVICE_ID_INTEL_HDA_SKL_LP,
+       },
+       {
+               .flags = FLAG_SST,
+               .device = PCI_DEVICE_ID_INTEL_HDA_KBL_LP,
+               .dmi_table = (const struct dmi_system_id []) {
+                       {
+                               .ident = "Google Chromebooks",
+                               .matches = {
+                                       DMI_MATCH(DMI_SYS_VENDOR, "Google"),
+                               }
+                       },
+                       {}
+               }
+       },
+       {
+               .flags = FLAG_SST | FLAG_SST_ONLY_IF_DMIC,
+               .device = PCI_DEVICE_ID_INTEL_HDA_KBL_LP,
+       },
+       {
+               .flags = FLAG_SST,
+               .device = PCI_DEVICE_ID_INTEL_HDA_APL,
+               .dmi_table = (const struct dmi_system_id []) {
+                       {
+                               .ident = "Google Chromebooks",
+                               .matches = {
+                                       DMI_MATCH(DMI_SYS_VENDOR, "Google"),
+                               }
+                       },
+                       {}
+               }
+       },
+       {
+               .flags = FLAG_SST,
+               .device = PCI_DEVICE_ID_INTEL_HDA_RPL_M,
+       },
+       {
+               .flags = FLAG_SST,
+               .device = PCI_DEVICE_ID_INTEL_HDA_FCL,
+       },
+#endif
+#if IS_ENABLED(CONFIG_SND_SOC_SOF_APOLLOLAKE)
+       {
+               .flags = FLAG_SOF,
+               .device = PCI_DEVICE_ID_INTEL_HDA_APL,
+               .dmi_table = (const struct dmi_system_id []) {
+                       {
+                               .ident = "Up Squared",
+                               .matches = {
+                                       DMI_MATCH(DMI_SYS_VENDOR, "AAEON"),
+                                       DMI_MATCH(DMI_BOARD_NAME, "UP-APL01"),
+                               }
+                       },
+                       {}
+               }
+       },
+       {
+               .flags = FLAG_SOF,
+               .device = PCI_DEVICE_ID_INTEL_HDA_APL,
+               .codec_hid =  &essx_83x6,
+       },
+#endif
+
+/*
+ * Geminilake uses legacy HDAudio driver except for Google
+ * Chromebooks and devices based on the ES8336 codec
+ */
+/* Geminilake */
+#if IS_ENABLED(CONFIG_SND_SOC_SOF_GEMINILAKE)
+       {
+               .flags = FLAG_SOF,
+               .device = PCI_DEVICE_ID_INTEL_HDA_GML,
+               .dmi_table = (const struct dmi_system_id []) {
+                       {
+                               .ident = "Google Chromebooks",
+                               .matches = {
+                                       DMI_MATCH(DMI_SYS_VENDOR, "Google"),
+                               }
+                       },
+                       {}
+               }
+       },
+       {
+               .flags = FLAG_SOF,
+               .device = PCI_DEVICE_ID_INTEL_HDA_GML,
+               .codec_hid =  &essx_83x6,
+       },
+#endif
+
+/*
+ * CoffeeLake, CannonLake, CometLake, IceLake, TigerLake, AlderLake,
+ * RaptorLake use legacy HDAudio driver except for Google Chromebooks
+ * and when DMICs are present. Two cases are required since Coreboot
+ * does not expose NHLT tables.
+ *
+ * When the Chromebook quirk is not present, it's based on information
+ * that no such device exists. When the quirk is present, it could be
+ * either based on product information or a placeholder.
+ */
+
+/* Cannonlake */
+#if IS_ENABLED(CONFIG_SND_SOC_SOF_CANNONLAKE)
+       {
+               .flags = FLAG_SOF,
+               .device = PCI_DEVICE_ID_INTEL_HDA_CNL_LP,
+               .dmi_table = (const struct dmi_system_id []) {
+                       {
+                               .ident = "Google Chromebooks",
+                               .matches = {
+                                       DMI_MATCH(DMI_SYS_VENDOR, "Google"),
+                               }
+                       },
+                       {
+                               .ident = "UP-WHL",
+                               .matches = {
+                                       DMI_MATCH(DMI_SYS_VENDOR, "AAEON"),
+                               }
+                       },
+                       {}
+               }
+       },
+       {
+               .flags = FLAG_SOF,
+               .device = PCI_DEVICE_ID_INTEL_HDA_CNL_LP,
+               .codec_hid =  &essx_83x6,
+       },
+       {
+               .flags = FLAG_SOF | FLAG_SOF_ONLY_IF_DMIC_OR_SOUNDWIRE,
+               .device = PCI_DEVICE_ID_INTEL_HDA_CNL_LP,
+       },
+#endif
+
+/* Coffelake */
+#if IS_ENABLED(CONFIG_SND_SOC_SOF_COFFEELAKE)
+       {
+               .flags = FLAG_SOF,
+               .device = PCI_DEVICE_ID_INTEL_HDA_CNL_H,
+               .dmi_table = (const struct dmi_system_id []) {
+                       {
+                               .ident = "Google Chromebooks",
+                               .matches = {
+                                       DMI_MATCH(DMI_SYS_VENDOR, "Google"),
+                               }
+                       },
+                       {}
+               }
+       },
+       {
+               .flags = FLAG_SOF | FLAG_SOF_ONLY_IF_DMIC_OR_SOUNDWIRE,
+               .device = PCI_DEVICE_ID_INTEL_HDA_CNL_H,
+       },
+#endif
+
+#if IS_ENABLED(CONFIG_SND_SOC_SOF_COMETLAKE)
+/* Cometlake-LP */
+       {
+               .flags = FLAG_SOF,
+               .device = PCI_DEVICE_ID_INTEL_HDA_CML_LP,
+               .dmi_table = (const struct dmi_system_id []) {
+                       {
+                               .ident = "Google Chromebooks",
+                               .matches = {
+                                       DMI_MATCH(DMI_SYS_VENDOR, "Google"),
+                               }
+                       },
+                       {
+                               .matches = {
+                                       DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc"),
+                                       DMI_EXACT_MATCH(DMI_PRODUCT_SKU, "09C6")
+                               },
+                       },
+                       {
+                               /* early version of SKU 09C6 */
+                               .matches = {
+                                       DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc"),
+                                       DMI_EXACT_MATCH(DMI_PRODUCT_SKU, "0983")
+                               },
+                       },
+                       {}
+               }
+       },
+       {
+               .flags = FLAG_SOF,
+               .device = PCI_DEVICE_ID_INTEL_HDA_CML_LP,
+               .codec_hid =  &essx_83x6,
+       },
+       {
+               .flags = FLAG_SOF | FLAG_SOF_ONLY_IF_DMIC_OR_SOUNDWIRE,
+               .device = PCI_DEVICE_ID_INTEL_HDA_CML_LP,
+       },
+/* Cometlake-H */
+       {
+               .flags = FLAG_SOF,
+               .device = PCI_DEVICE_ID_INTEL_HDA_CML_H,
+               .dmi_table = (const struct dmi_system_id []) {
+                       {
+                               .matches = {
+                                       DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc"),
+                                       DMI_EXACT_MATCH(DMI_PRODUCT_SKU, "098F"),
+                               },
+                       },
+                       {
+                               .matches = {
+                                       DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc"),
+                                       DMI_EXACT_MATCH(DMI_PRODUCT_SKU, "0990"),
+                               },
+                       },
+                       {}
+               }
+       },
+       {
+               .flags = FLAG_SOF,
+               .device = PCI_DEVICE_ID_INTEL_HDA_CML_H,
+               .codec_hid =  &essx_83x6,
+       },
+       {
+               .flags = FLAG_SOF | FLAG_SOF_ONLY_IF_DMIC_OR_SOUNDWIRE,
+               .device = PCI_DEVICE_ID_INTEL_HDA_CML_H,
+       },
+#endif
+
+/* Icelake */
+#if IS_ENABLED(CONFIG_SND_SOC_SOF_ICELAKE)
+       {
+               .flags = FLAG_SOF,
+               .device = PCI_DEVICE_ID_INTEL_HDA_ICL_LP,
+               .dmi_table = (const struct dmi_system_id []) {
+                       {
+                               .ident = "Google Chromebooks",
+                               .matches = {
+                                       DMI_MATCH(DMI_SYS_VENDOR, "Google"),
+                               }
+                       },
+                       {}
+               }
+       },
+       {
+               .flags = FLAG_SOF,
+               .device = PCI_DEVICE_ID_INTEL_HDA_ICL_LP,
+               .codec_hid =  &essx_83x6,
+       },
+       {
+               .flags = FLAG_SOF | FLAG_SOF_ONLY_IF_DMIC_OR_SOUNDWIRE,
+               .device = PCI_DEVICE_ID_INTEL_HDA_ICL_LP,
+       },
+#endif
+
+/* Jasper Lake */
+#if IS_ENABLED(CONFIG_SND_SOC_SOF_JASPERLAKE)
+       {
+               .flags = FLAG_SOF,
+               .device = PCI_DEVICE_ID_INTEL_HDA_JSL_N,
+               .dmi_table = (const struct dmi_system_id []) {
+                       {
+                               .ident = "Google Chromebooks",
+                               .matches = {
+                                       DMI_MATCH(DMI_SYS_VENDOR, "Google"),
+                               }
+                       },
+                       {
+                               .ident = "Google firmware",
+                               .matches = {
+                                       DMI_MATCH(DMI_BIOS_VERSION, "Google"),
+                               }
+                       },
+                       {}
+               }
+       },
+       {
+               .flags = FLAG_SOF,
+               .device = PCI_DEVICE_ID_INTEL_HDA_JSL_N,
+               .codec_hid =  &essx_83x6,
+       },
+       {
+               .flags = FLAG_SOF | FLAG_SOF_ONLY_IF_DMIC,
+               .device = PCI_DEVICE_ID_INTEL_HDA_JSL_N,
+       },
+#endif
+
+/* Tigerlake */
+#if IS_ENABLED(CONFIG_SND_SOC_SOF_TIGERLAKE)
+       {
+               .flags = FLAG_SOF,
+               .device = PCI_DEVICE_ID_INTEL_HDA_TGL_LP,
+               .dmi_table = (const struct dmi_system_id []) {
+                       {
+                               .ident = "Google Chromebooks",
+                               .matches = {
+                                       DMI_MATCH(DMI_SYS_VENDOR, "Google"),
+                               }
+                       },
+                       {
+                               .ident = "UPX-TGL",
+                               .matches = {
+                                       DMI_MATCH(DMI_SYS_VENDOR, "AAEON"),
+                               }
+                       },
+                       {}
+               }
+       },
+       {
+               .flags = FLAG_SOF,
+               .device = PCI_DEVICE_ID_INTEL_HDA_TGL_LP,
+               .codec_hid =  &essx_83x6,
+       },
+       {
+               .flags = FLAG_SOF | FLAG_SOF_ONLY_IF_DMIC_OR_SOUNDWIRE,
+               .device = PCI_DEVICE_ID_INTEL_HDA_TGL_LP,
+       },
+       {
+               .flags = FLAG_SOF | FLAG_SOF_ONLY_IF_DMIC_OR_SOUNDWIRE,
+               .device = PCI_DEVICE_ID_INTEL_HDA_TGL_H,
+       },
+#endif
+
+/* Elkhart Lake */
+#if IS_ENABLED(CONFIG_SND_SOC_SOF_ELKHARTLAKE)
+       {
+               .flags = FLAG_SOF | FLAG_SOF_ONLY_IF_DMIC,
+               .device = PCI_DEVICE_ID_INTEL_HDA_EHL_0,
+       },
+       {
+               .flags = FLAG_SOF | FLAG_SOF_ONLY_IF_DMIC,
+               .device = PCI_DEVICE_ID_INTEL_HDA_EHL_3,
+       },
+#endif
+
+/* Alder Lake / Raptor Lake */
+#if IS_ENABLED(CONFIG_SND_SOC_SOF_ALDERLAKE)
+       {
+               .flags = FLAG_SOF | FLAG_SOF_ONLY_IF_DMIC_OR_SOUNDWIRE,
+               .device = PCI_DEVICE_ID_INTEL_HDA_ADL_S,
+       },
+       {
+               .flags = FLAG_SOF | FLAG_SOF_ONLY_IF_DMIC_OR_SOUNDWIRE,
+               .device = PCI_DEVICE_ID_INTEL_HDA_RPL_S,
+       },
+       {
+               .flags = FLAG_SOF,
+               .device = PCI_DEVICE_ID_INTEL_HDA_ADL_P,
+               .dmi_table = (const struct dmi_system_id []) {
+                       {
+                               .ident = "Google Chromebooks",
+                               .matches = {
+                                       DMI_MATCH(DMI_SYS_VENDOR, "Google"),
+                               }
+                       },
+                       {}
+               }
+       },
+       {
+               .flags = FLAG_SOF,
+               .device = PCI_DEVICE_ID_INTEL_HDA_ADL_P,
+               .codec_hid =  &essx_83x6,
+       },
+       {
+               .flags = FLAG_SOF | FLAG_SOF_ONLY_IF_DMIC_OR_SOUNDWIRE,
+               .device = PCI_DEVICE_ID_INTEL_HDA_ADL_P,
+       },
+       {
+               .flags = FLAG_SOF | FLAG_SOF_ONLY_IF_DMIC_OR_SOUNDWIRE,
+               .device = PCI_DEVICE_ID_INTEL_HDA_ADL_PX,
+       },
+       {
+               .flags = FLAG_SOF,
+               .device = PCI_DEVICE_ID_INTEL_HDA_ADL_PS,
+               .codec_hid =  &essx_83x6,
+       },
+       {
+               .flags = FLAG_SOF | FLAG_SOF_ONLY_IF_DMIC_OR_SOUNDWIRE,
+               .device = PCI_DEVICE_ID_INTEL_HDA_ADL_PS,
+       },
+       {
+               .flags = FLAG_SOF | FLAG_SOF_ONLY_IF_DMIC_OR_SOUNDWIRE,
+               .device = PCI_DEVICE_ID_INTEL_HDA_ADL_M,
+       },
+       {
+               .flags = FLAG_SOF,
+               .device = PCI_DEVICE_ID_INTEL_HDA_ADL_N,
+               .dmi_table = (const struct dmi_system_id []) {
+                       {
+                               .ident = "Google Chromebooks",
+                               .matches = {
+                                       DMI_MATCH(DMI_SYS_VENDOR, "Google"),
+                               }
+                       },
+                       {}
+               }
+       },
+       {
+               .flags = FLAG_SOF | FLAG_SOF_ONLY_IF_DMIC_OR_SOUNDWIRE,
+               .device = PCI_DEVICE_ID_INTEL_HDA_ADL_N,
+       },
+       {
+               .flags = FLAG_SOF,
+               .device = PCI_DEVICE_ID_INTEL_HDA_RPL_P_0,
+               .dmi_table = (const struct dmi_system_id []) {
+                       {
+                               .ident = "Google Chromebooks",
+                               .matches = {
+                                       DMI_MATCH(DMI_SYS_VENDOR, "Google"),
+                               }
+                       },
+                       {}
+               }
+       },
+       {
+               .flags = FLAG_SOF | FLAG_SOF_ONLY_IF_DMIC_OR_SOUNDWIRE,
+               .device = PCI_DEVICE_ID_INTEL_HDA_RPL_P_0,
+       },
+       {
+               .flags = FLAG_SOF,
+               .device = PCI_DEVICE_ID_INTEL_HDA_RPL_P_1,
+               .dmi_table = (const struct dmi_system_id []) {
+                       {
+                               .ident = "Google Chromebooks",
+                               .matches = {
+                                       DMI_MATCH(DMI_SYS_VENDOR, "Google"),
+                               }
+                       },
+                       {}
+               }
+       },
+       {
+               .flags = FLAG_SOF | FLAG_SOF_ONLY_IF_DMIC_OR_SOUNDWIRE,
+               .device = PCI_DEVICE_ID_INTEL_HDA_RPL_P_1,
+       },
+       {
+               .flags = FLAG_SOF | FLAG_SOF_ONLY_IF_DMIC_OR_SOUNDWIRE,
+               .device = PCI_DEVICE_ID_INTEL_HDA_RPL_M,
+       },
+       {
+               .flags = FLAG_SOF | FLAG_SOF_ONLY_IF_DMIC_OR_SOUNDWIRE,
+               .device = PCI_DEVICE_ID_INTEL_HDA_RPL_PX,
+       },
+#endif
+
+/* Meteor Lake */
+#if IS_ENABLED(CONFIG_SND_SOC_SOF_METEORLAKE)
+       /* Meteorlake-P */
+       {
+               .flags = FLAG_SOF | FLAG_SOF_ONLY_IF_DMIC_OR_SOUNDWIRE,
+               .device = PCI_DEVICE_ID_INTEL_HDA_MTL,
+       },
+       /* ArrowLake-S */
+       {
+               .flags = FLAG_SOF | FLAG_SOF_ONLY_IF_DMIC_OR_SOUNDWIRE,
+               .device = PCI_DEVICE_ID_INTEL_HDA_ARL_S,
+       },
+       /* ArrowLake */
+       {
+               .flags = FLAG_SOF | FLAG_SOF_ONLY_IF_DMIC_OR_SOUNDWIRE,
+               .device = PCI_DEVICE_ID_INTEL_HDA_ARL,
+       },
+#endif
+
+/* Lunar Lake */
+#if IS_ENABLED(CONFIG_SND_SOC_SOF_LUNARLAKE)
+       /* Lunarlake-P */
+       {
+               .flags = FLAG_SOF | FLAG_SOF_ONLY_IF_DMIC_OR_SOUNDWIRE,
+               .device = PCI_DEVICE_ID_INTEL_HDA_LNL_P,
+       },
+#endif
+
+       /* Panther Lake, Wildcat Lake */
+#if IS_ENABLED(CONFIG_SND_SOC_SOF_PANTHERLAKE)
+       {
+               .flags = FLAG_SOF | FLAG_SOF_ONLY_IF_DMIC_OR_SOUNDWIRE,
+               .device = PCI_DEVICE_ID_INTEL_HDA_PTL,
+       },
+       {
+               .flags = FLAG_SOF | FLAG_SOF_ONLY_IF_DMIC_OR_SOUNDWIRE,
+               .device = PCI_DEVICE_ID_INTEL_HDA_PTL_H,
+       },
+       {
+               .flags = FLAG_SOF | FLAG_SOF_ONLY_IF_DMIC_OR_SOUNDWIRE,
+               .device = PCI_DEVICE_ID_INTEL_HDA_WCL,
+       },
+
+#endif
+
+};
+
+static const struct config_entry *snd_intel_dsp_find_config
+               (struct pci_dev *pci, const struct config_entry *table, u32 len)
+{
+       u16 device;
+
+       device = pci->device;
+       for (; len > 0; len--, table++) {
+               if (table->device != device)
+                       continue;
+               if (table->dmi_table && !dmi_check_system(table->dmi_table))
+                       continue;
+               if (table->codec_hid) {
+                       int i;
+
+                       for (i = 0; i < table->codec_hid->num_codecs; i++) {
+                               struct nhlt_acpi_table *nhlt;
+                               bool ssp_found = false;
+
+                               if (!acpi_dev_present(table->codec_hid->codecs[i], NULL, -1))
+                                       continue;
+
+                               nhlt = intel_nhlt_init(&pci->dev);
+                               if (!nhlt) {
+                                       dev_warn(&pci->dev, "%s: NHLT table not found, skipped HID %s\n",
+                                                __func__, table->codec_hid->codecs[i]);
+                                       continue;
+                               }
+
+                               if (intel_nhlt_has_endpoint_type(nhlt, NHLT_LINK_SSP) &&
+                                   intel_nhlt_ssp_endpoint_mask(nhlt, NHLT_DEVICE_I2S))
+                                       ssp_found = true;
+
+                               intel_nhlt_free(nhlt);
+
+                               if (ssp_found)
+                                       break;
+
+                               dev_warn(&pci->dev, "%s: no valid SSP found for HID %s, skipped\n",
+                                        __func__, table->codec_hid->codecs[i]);
+                       }
+                       if (i == table->codec_hid->num_codecs)
+                               continue;
+               }
+               return table;
+       }
+       return NULL;
+}
+
+static int snd_intel_dsp_check_dmic(struct pci_dev *pci)
+{
+       int ret = 0;
+
+       acpi_nhlt_get_gbl_table();
+
+       if (acpi_nhlt_find_endpoint(ACPI_NHLT_LINKTYPE_PDM, -1, -1, -1))
+               ret = 1;
+
+       acpi_nhlt_put_gbl_table();
+
+       return ret;
+}
+
+#if IS_ENABLED(CONFIG_SND_SOC_SOF_INTEL_SOUNDWIRE)
+static int snd_intel_dsp_check_soundwire(struct pci_dev *pci)
+{
+       struct sdw_intel_acpi_info info;
+       acpi_handle handle;
+       int ret;
+
+       handle = ACPI_HANDLE(&pci->dev);
+
+       ret = sdw_intel_acpi_scan(handle, &info);
+       if (ret < 0)
+               return ret;
+
+       return info.link_mask;
+}
+#else
+static int snd_intel_dsp_check_soundwire(struct pci_dev *pci)
+{
+       return 0;
+}
+#endif
+
+int snd_intel_dsp_driver_probe(struct pci_dev *pci)
+{
+       const struct config_entry *cfg;
+
+       /* Intel vendor only */
+       if (pci->vendor != PCI_VENDOR_ID_INTEL)
+               return SND_INTEL_DSP_DRIVER_ANY;
+
+       /*
+        * Legacy devices don't have a PCI-based DSP and use HDaudio
+        * for HDMI/DP support, ignore kernel parameter
+        */
+       switch (pci->device) {
+       case PCI_DEVICE_ID_INTEL_HDA_BDW:
+       case PCI_DEVICE_ID_INTEL_HDA_HSW_0:
+       case PCI_DEVICE_ID_INTEL_HDA_HSW_2:
+       case PCI_DEVICE_ID_INTEL_HDA_HSW_3:
+       case PCI_DEVICE_ID_INTEL_HDA_BYT:
+       case PCI_DEVICE_ID_INTEL_HDA_BSW:
+               return SND_INTEL_DSP_DRIVER_ANY;
+       }
+
+       if (dsp_driver > 0 && dsp_driver <= SND_INTEL_DSP_DRIVER_LAST)
+               return dsp_driver;
+
+       /*
+        * detect DSP by checking class/subclass/prog-id information
+        * class=04 subclass 03 prog-if 00: no DSP, use legacy driver
+        * class=04 subclass 01 prog-if 00: DSP is present
+        *  (and may be required e.g. for DMIC or SSP support)
+        * class=04 subclass 03 prog-if 80: use DSP or legacy mode
+        */
+       if (pci->class == 0x040300)
+               return SND_INTEL_DSP_DRIVER_LEGACY;
+       if (pci->class != 0x040100 && pci->class != 0x040380) {
+               dev_err(&pci->dev, "Unknown PCI class/subclass/prog-if information (0x%06x) found, selecting HDAudio legacy driver\n", pci->class);
+               return SND_INTEL_DSP_DRIVER_LEGACY;
+       }
+
+       dev_dbg(&pci->dev, "DSP detected with PCI class/subclass/prog-if info 0x%06x\n", pci->class);
+
+       /* find the configuration for the specific device */
+       cfg = snd_intel_dsp_find_config(pci, config_table, ARRAY_SIZE(config_table));
+       if (!cfg)
+               return SND_INTEL_DSP_DRIVER_ANY;
+
+       if (cfg->flags & FLAG_SOF) {
+               if (cfg->flags & FLAG_SOF_ONLY_IF_SOUNDWIRE &&
+                   snd_intel_dsp_check_soundwire(pci) > 0) {
+                       dev_info_once(&pci->dev, "SoundWire enabled on CannonLake+ platform, using SOF driver\n");
+                       return SND_INTEL_DSP_DRIVER_SOF;
+               }
+               if (cfg->flags & FLAG_SOF_ONLY_IF_DMIC &&
+                   snd_intel_dsp_check_dmic(pci)) {
+                       dev_info_once(&pci->dev, "Digital mics found on Skylake+ platform, using SOF driver\n");
+                       return SND_INTEL_DSP_DRIVER_SOF;
+               }
+               if (!(cfg->flags & FLAG_SOF_ONLY_IF_DMIC_OR_SOUNDWIRE))
+                       return SND_INTEL_DSP_DRIVER_SOF;
+       }
+
+
+       if (cfg->flags & FLAG_SST) {
+               if (cfg->flags & FLAG_SST_ONLY_IF_DMIC) {
+                       if (snd_intel_dsp_check_dmic(pci)) {
+                               dev_info_once(&pci->dev, "Digital mics found on Skylake+ platform, using SST driver\n");
+                               return SND_INTEL_DSP_DRIVER_SST;
+                       }
+               } else {
+                       return SND_INTEL_DSP_DRIVER_SST;
+               }
+       }
+
+       return SND_INTEL_DSP_DRIVER_LEGACY;
+}
+EXPORT_SYMBOL_GPL(snd_intel_dsp_driver_probe);
+
+/* Should we default to SOF or SST for BYT/CHT ? */
+#if IS_ENABLED(CONFIG_SND_INTEL_BYT_PREFER_SOF) || \
+    !IS_ENABLED(CONFIG_SND_SST_ATOM_HIFI2_PLATFORM_ACPI)
+#define FLAG_SST_OR_SOF_BYT    FLAG_SOF
+#else
+#define FLAG_SST_OR_SOF_BYT    FLAG_SST
+#endif
+
+/*
+ * configuration table
+ * - the order of similar ACPI ID entries is important!
+ * - the first successful match will win
+ */
+static const struct config_entry acpi_config_table[] = {
+#if IS_ENABLED(CONFIG_SND_SST_ATOM_HIFI2_PLATFORM_ACPI) || \
+    IS_ENABLED(CONFIG_SND_SOC_SOF_BAYTRAIL)
+/* BayTrail */
+       {
+               .flags = FLAG_SST_OR_SOF_BYT,
+               .acpi_hid = "LPE0F28",
+       },
+       {
+               .flags = FLAG_SST_OR_SOF_BYT,
+               .acpi_hid = "80860F28",
+       },
+/* CherryTrail */
+       {
+               .flags = FLAG_SST_OR_SOF_BYT,
+               .acpi_hid = "808622A8",
+       },
+#endif
+/* Broadwell */
+#if IS_ENABLED(CONFIG_SND_SOC_INTEL_CATPT)
+       {
+               .flags = FLAG_SST,
+               .acpi_hid = "INT3438"
+       },
+#endif
+#if IS_ENABLED(CONFIG_SND_SOC_SOF_BROADWELL)
+       {
+               .flags = FLAG_SOF,
+               .acpi_hid = "INT3438"
+       },
+#endif
+/* Haswell - not supported by SOF but added for consistency */
+#if IS_ENABLED(CONFIG_SND_SOC_INTEL_CATPT)
+       {
+               .flags = FLAG_SST,
+               .acpi_hid = "INT33C8"
+       },
+#endif
+};
+
+static const struct config_entry *snd_intel_acpi_dsp_find_config(const u8 acpi_hid[ACPI_ID_LEN],
+                                                                const struct config_entry *table,
+                                                                u32 len)
+{
+       for (; len > 0; len--, table++) {
+               if (memcmp(table->acpi_hid, acpi_hid, ACPI_ID_LEN))
+                       continue;
+               if (table->dmi_table && !dmi_check_system(table->dmi_table))
+                       continue;
+               return table;
+       }
+       return NULL;
+}
+
+int snd_intel_acpi_dsp_driver_probe(struct device *dev, const u8 acpi_hid[ACPI_ID_LEN])
+{
+       const struct config_entry *cfg;
+
+       if (dsp_driver > SND_INTEL_DSP_DRIVER_LEGACY && dsp_driver <= SND_INTEL_DSP_DRIVER_LAST)
+               return dsp_driver;
+
+       if (dsp_driver == SND_INTEL_DSP_DRIVER_LEGACY) {
+               dev_warn(dev, "dsp_driver parameter %d not supported, using automatic detection\n",
+                        SND_INTEL_DSP_DRIVER_LEGACY);
+       }
+
+       /* find the configuration for the specific device */
+       cfg = snd_intel_acpi_dsp_find_config(acpi_hid,  acpi_config_table,
+                                            ARRAY_SIZE(acpi_config_table));
+       if (!cfg)
+               return SND_INTEL_DSP_DRIVER_ANY;
+
+       if (cfg->flags & FLAG_SST)
+               return SND_INTEL_DSP_DRIVER_SST;
+
+       if (cfg->flags & FLAG_SOF)
+               return SND_INTEL_DSP_DRIVER_SOF;
+
+       return SND_INTEL_DSP_DRIVER_SST;
+}
+EXPORT_SYMBOL_GPL(snd_intel_acpi_dsp_driver_probe);
+
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("Intel DSP config driver");
+MODULE_IMPORT_NS("SND_INTEL_SOUNDWIRE_ACPI");
diff --git a/sound/hda/core/intel-nhlt.c b/sound/hda/core/intel-nhlt.c
new file mode 100644 (file)
index 0000000..6d72a87
--- /dev/null
@@ -0,0 +1,388 @@
+// SPDX-License-Identifier: GPL-2.0-only
+// Copyright (c) 2015-2019 Intel Corporation
+
+#include <linux/acpi.h>
+#include <sound/intel-nhlt.h>
+
+struct nhlt_acpi_table *intel_nhlt_init(struct device *dev)
+{
+       struct nhlt_acpi_table *nhlt;
+       acpi_status status;
+
+       status = acpi_get_table(ACPI_SIG_NHLT, 0,
+                               (struct acpi_table_header **)&nhlt);
+       if (ACPI_FAILURE(status)) {
+               dev_warn(dev, "NHLT table not found\n");
+               return NULL;
+       }
+
+       return nhlt;
+}
+EXPORT_SYMBOL_GPL(intel_nhlt_init);
+
+void intel_nhlt_free(struct nhlt_acpi_table *nhlt)
+{
+       acpi_put_table((struct acpi_table_header *)nhlt);
+}
+EXPORT_SYMBOL_GPL(intel_nhlt_free);
+
+int intel_nhlt_get_dmic_geo(struct device *dev, struct nhlt_acpi_table *nhlt)
+{
+       struct nhlt_endpoint *epnt;
+       struct nhlt_dmic_array_config *cfg;
+       struct nhlt_vendor_dmic_array_config *cfg_vendor;
+       struct nhlt_fmt *fmt_configs;
+       unsigned int dmic_geo = 0;
+       u16 max_ch = 0;
+       u8 i, j;
+
+       if (!nhlt)
+               return 0;
+
+       if (nhlt->header.length <= sizeof(struct acpi_table_header)) {
+               dev_warn(dev, "Invalid DMIC description table\n");
+               return 0;
+       }
+
+       for (j = 0, epnt = nhlt->desc; j < nhlt->endpoint_count; j++,
+            epnt = (struct nhlt_endpoint *)((u8 *)epnt + epnt->length)) {
+
+               if (epnt->linktype != NHLT_LINK_DMIC)
+                       continue;
+
+               cfg = (struct nhlt_dmic_array_config  *)(epnt->config.caps);
+               fmt_configs = (struct nhlt_fmt *)(epnt->config.caps + epnt->config.size);
+
+               /* find max number of channels based on format_configuration */
+               if (fmt_configs->fmt_count) {
+                       struct nhlt_fmt_cfg *fmt_cfg = fmt_configs->fmt_config;
+
+                       dev_dbg(dev, "found %d format definitions\n",
+                               fmt_configs->fmt_count);
+
+                       for (i = 0; i < fmt_configs->fmt_count; i++) {
+                               struct wav_fmt_ext *fmt_ext;
+
+                               fmt_ext = &fmt_cfg->fmt_ext;
+
+                               if (fmt_ext->fmt.channels > max_ch)
+                                       max_ch = fmt_ext->fmt.channels;
+
+                               /* Move to the next nhlt_fmt_cfg */
+                               fmt_cfg = (struct nhlt_fmt_cfg *)(fmt_cfg->config.caps +
+                                                                 fmt_cfg->config.size);
+                       }
+                       dev_dbg(dev, "max channels found %d\n", max_ch);
+               } else {
+                       dev_dbg(dev, "No format information found\n");
+               }
+
+               if (cfg->device_config.config_type != NHLT_CONFIG_TYPE_MIC_ARRAY) {
+                       dmic_geo = max_ch;
+               } else {
+                       switch (cfg->array_type) {
+                       case NHLT_MIC_ARRAY_2CH_SMALL:
+                       case NHLT_MIC_ARRAY_2CH_BIG:
+                               dmic_geo = MIC_ARRAY_2CH;
+                               break;
+
+                       case NHLT_MIC_ARRAY_4CH_1ST_GEOM:
+                       case NHLT_MIC_ARRAY_4CH_L_SHAPED:
+                       case NHLT_MIC_ARRAY_4CH_2ND_GEOM:
+                               dmic_geo = MIC_ARRAY_4CH;
+                               break;
+                       case NHLT_MIC_ARRAY_VENDOR_DEFINED:
+                               cfg_vendor = (struct nhlt_vendor_dmic_array_config *)cfg;
+                               dmic_geo = cfg_vendor->nb_mics;
+                               break;
+                       default:
+                               dev_warn(dev, "%s: undefined DMIC array_type 0x%0x\n",
+                                        __func__, cfg->array_type);
+                       }
+
+                       if (dmic_geo > 0) {
+                               dev_dbg(dev, "Array with %d dmics\n", dmic_geo);
+                       }
+                       if (max_ch > dmic_geo) {
+                               dev_dbg(dev, "max channels %d exceed dmic number %d\n",
+                                       max_ch, dmic_geo);
+                       }
+               }
+       }
+
+       dev_dbg(dev, "dmic number %d max_ch %d\n", dmic_geo, max_ch);
+
+       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);
+
+int intel_nhlt_ssp_endpoint_mask(struct nhlt_acpi_table *nhlt, u8 device_type)
+{
+       struct nhlt_endpoint *epnt;
+       int ssp_mask = 0;
+       int i;
+
+       if (!nhlt || (device_type != NHLT_DEVICE_BT && device_type != NHLT_DEVICE_I2S))
+               return 0;
+
+       epnt = (struct nhlt_endpoint *)nhlt->desc;
+       for (i = 0; i < nhlt->endpoint_count; i++) {
+               if (epnt->linktype == NHLT_LINK_SSP && epnt->device_type == device_type) {
+                       /* for SSP the virtual bus id is the SSP port */
+                       ssp_mask |= BIT(epnt->virtual_bus_id);
+               }
+               epnt = (struct nhlt_endpoint *)((u8 *)epnt + epnt->length);
+       }
+
+       return ssp_mask;
+}
+EXPORT_SYMBOL(intel_nhlt_ssp_endpoint_mask);
+
+#define SSP_BLOB_V1_0_SIZE             84
+#define SSP_BLOB_V1_0_MDIVC_OFFSET     19 /* offset in u32 */
+
+#define SSP_BLOB_V1_5_SIZE             96
+#define SSP_BLOB_V1_5_MDIVC_OFFSET     21 /* offset in u32 */
+#define SSP_BLOB_VER_1_5               0xEE000105
+
+#define SSP_BLOB_V2_0_SIZE             88
+#define SSP_BLOB_V2_0_MDIVC_OFFSET     20 /* offset in u32 */
+#define SSP_BLOB_VER_2_0               0xEE000200
+
+int intel_nhlt_ssp_mclk_mask(struct nhlt_acpi_table *nhlt, int ssp_num)
+{
+       struct nhlt_endpoint *epnt;
+       struct nhlt_fmt *fmt;
+       struct nhlt_fmt_cfg *cfg;
+       int mclk_mask = 0;
+       int i, j;
+
+       if (!nhlt)
+               return 0;
+
+       epnt = (struct nhlt_endpoint *)nhlt->desc;
+       for (i = 0; i < nhlt->endpoint_count; i++) {
+
+               /* we only care about endpoints connected to an audio codec over SSP */
+               if (epnt->linktype == NHLT_LINK_SSP &&
+                   epnt->device_type == NHLT_DEVICE_I2S &&
+                   epnt->virtual_bus_id == ssp_num) {
+
+                       fmt = (struct nhlt_fmt *)(epnt->config.caps + epnt->config.size);
+                       cfg = fmt->fmt_config;
+
+                       /*
+                        * In theory all formats should use the same MCLK but it doesn't hurt to
+                        * double-check that the configuration is consistent
+                        */
+                       for (j = 0; j < fmt->fmt_count; j++) {
+                               u32 *blob;
+                               int mdivc_offset;
+                               int size;
+
+                               /* first check we have enough data to read the blob type */
+                               if (cfg->config.size < 8)
+                                       return -EINVAL;
+
+                               blob = (u32 *)cfg->config.caps;
+
+                               if (blob[1] == SSP_BLOB_VER_2_0) {
+                                       mdivc_offset = SSP_BLOB_V2_0_MDIVC_OFFSET;
+                                       size = SSP_BLOB_V2_0_SIZE;
+                               } else if (blob[1] == SSP_BLOB_VER_1_5) {
+                                       mdivc_offset = SSP_BLOB_V1_5_MDIVC_OFFSET;
+                                       size = SSP_BLOB_V1_5_SIZE;
+                               } else {
+                                       mdivc_offset = SSP_BLOB_V1_0_MDIVC_OFFSET;
+                                       size = SSP_BLOB_V1_0_SIZE;
+                               }
+
+                               /* make sure we have enough data for the fixed part of the blob */
+                               if (cfg->config.size < size)
+                                       return -EINVAL;
+
+                               mclk_mask |=  blob[mdivc_offset] & GENMASK(1, 0);
+
+                               cfg = (struct nhlt_fmt_cfg *)(cfg->config.caps + cfg->config.size);
+                       }
+               }
+               epnt = (struct nhlt_endpoint *)((u8 *)epnt + epnt->length);
+       }
+
+       /* make sure only one MCLK is used */
+       if (hweight_long(mclk_mask) != 1)
+               return -EINVAL;
+
+       return mclk_mask;
+}
+EXPORT_SYMBOL(intel_nhlt_ssp_mclk_mask);
+
+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, bool ignore_vbps)
+{
+       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);
+
+               /*
+                * When looking for exact match of configuration ignore the vbps
+                * from NHLT table when ignore_vbps is true
+                */
+               if (wfmt->channels == num_ch && wfmt->samples_per_sec == rate &&
+                   (ignore_vbps || 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;
+       bool ignore_vbps = false;
+       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);
+       if (link_type == NHLT_LINK_DMIC && bps == 32 && (vbps == 24 || vbps == 32)) {
+               /*
+                * The DMIC hardware supports only one type of 32 bits sample
+                * size, which is 24 bit sampling on the MSB side and bits[1:0]
+                * are used for indicating the channel number.
+                * It has been observed that some NHLT tables have the vbps
+                * specified as 32 while some uses 24.
+                * The format these variations describe are identical, the
+                * hardware is configured and behaves the same way.
+                * Note: when the samples assumed to be vbps=32 then the 'noise'
+                * introduced by the lower two bits (channel number) have no
+                * real life implication on audio quality.
+                */
+               dev_dbg(dev,
+                       "  ch=%d fmt=%d rate=%d (vbps is ignored for DMIC 32bit format)\n",
+                       num_ch, bps, rate);
+               ignore_vbps = true;
+       } else {
+               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, ignore_vbps);
+                       if (cfg)
+                               return cfg;
+               }
+
+               epnt = (struct nhlt_endpoint *)((u8 *)epnt + epnt->length);
+       }
+
+       return NULL;
+}
+EXPORT_SYMBOL(intel_nhlt_get_endpoint_blob);
+
+int intel_nhlt_ssp_device_type(struct device *dev, struct nhlt_acpi_table *nhlt,
+                              u8 virtual_bus_id)
+{
+       struct nhlt_endpoint *epnt;
+       int i;
+
+       if (!nhlt) {
+               dev_err(dev, "%s: NHLT table is missing (query for SSP%d)\n",
+                       __func__, virtual_bus_id);
+               return -EINVAL;
+       }
+
+       epnt = (struct nhlt_endpoint *)nhlt->desc;
+       for (i = 0; i < nhlt->endpoint_count; i++) {
+               /* for SSP link the virtual bus id is the SSP port number */
+               if (epnt->linktype == NHLT_LINK_SSP &&
+                   epnt->virtual_bus_id == virtual_bus_id) {
+                       dev_dbg(dev, "SSP%d: dev_type=%d\n", virtual_bus_id,
+                               epnt->device_type);
+                       return epnt->device_type;
+               }
+
+               epnt = (struct nhlt_endpoint *)((u8 *)epnt + epnt->length);
+       }
+
+       dev_err(dev, "%s: No match for SSP%d in NHLT table\n", __func__,
+               virtual_bus_id);
+
+       dev_dbg(dev, "Available endpoints:\n");
+       epnt = (struct nhlt_endpoint *)nhlt->desc;
+       for (i = 0; i < nhlt->endpoint_count; i++) {
+               dev_dbg(dev,
+                       "%d: link_type: %d, vbus_id: %d, dir: %d, dev_type: %d\n",
+                       i, epnt->linktype, epnt->virtual_bus_id,
+                       epnt->direction, epnt->device_type);
+
+               epnt = (struct nhlt_endpoint *)((u8 *)epnt + epnt->length);
+       }
+
+       return -EINVAL;
+}
+EXPORT_SYMBOL(intel_nhlt_ssp_device_type);
diff --git a/sound/hda/core/intel-sdw-acpi.c b/sound/hda/core/intel-sdw-acpi.c
new file mode 100644 (file)
index 0000000..d351113
--- /dev/null
@@ -0,0 +1,204 @@
+// SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause)
+// Copyright(c) 2015-2021 Intel Corporation.
+
+/*
+ * SDW Intel ACPI scan helpers
+ */
+
+#include <linux/acpi.h>
+#include <linux/bits.h>
+#include <linux/bitfield.h>
+#include <linux/device.h>
+#include <linux/errno.h>
+#include <linux/export.h>
+#include <linux/module.h>
+#include <linux/property.h>
+#include <linux/soundwire/sdw_intel.h>
+#include <linux/string.h>
+
+#define SDW_LINK_TYPE          4 /* from Intel ACPI documentation */
+
+static int ctrl_link_mask;
+module_param_named(sdw_link_mask, ctrl_link_mask, int, 0444);
+MODULE_PARM_DESC(sdw_link_mask, "Intel link mask (one bit per link)");
+
+static ulong ctrl_addr = 0x40000000;
+module_param_named(sdw_ctrl_addr, ctrl_addr, ulong, 0444);
+MODULE_PARM_DESC(sdw_ctrl_addr, "Intel SoundWire Controller _ADR");
+
+static bool is_link_enabled(struct fwnode_handle *fw_node, u8 idx)
+{
+       struct fwnode_handle *link;
+       char name[32];
+       u32 quirk_mask = 0;
+
+       /* Find master handle */
+       snprintf(name, sizeof(name),
+                "mipi-sdw-link-%hhu-subproperties", idx);
+
+       link = fwnode_get_named_child_node(fw_node, name);
+       if (!link)
+               return false;
+
+       fwnode_property_read_u32(link,
+                                "intel-quirk-mask",
+                                &quirk_mask);
+
+       fwnode_handle_put(link);
+
+       if (quirk_mask & SDW_INTEL_QUIRK_MASK_BUS_DISABLE)
+               return false;
+
+       return true;
+}
+
+static int
+sdw_intel_scan_controller(struct sdw_intel_acpi_info *info)
+{
+       struct acpi_device *adev = acpi_fetch_acpi_dev(info->handle);
+       struct fwnode_handle *fwnode;
+       unsigned long list;
+       unsigned int i;
+       u32 count;
+       u32 tmp;
+       int ret;
+
+       if (!adev)
+               return -EINVAL;
+
+       fwnode = acpi_fwnode_handle(adev);
+
+       /*
+        * Found controller, find links supported
+        *
+        * In theory we could check the number of links supported in
+        * hardware, but in that step we cannot assume SoundWire IP is
+        * powered.
+        *
+        * In addition, if the BIOS doesn't even provide this
+        * 'master-count' property then all the inits based on link
+        * masks will fail as well.
+        *
+        * We will check the hardware capabilities in the startup() step
+        */
+       ret = fwnode_property_read_u32(fwnode, "mipi-sdw-manager-list", &tmp);
+       if (ret) {
+               ret = fwnode_property_read_u32(fwnode, "mipi-sdw-master-count", &count);
+               if (ret) {
+                       dev_err(&adev->dev,
+                               "Failed to read mipi-sdw-master-count: %d\n",
+                               ret);
+                       return ret;
+               }
+               list = GENMASK(count - 1, 0);
+       } else {
+               list = tmp;
+               count = hweight32(list);
+       }
+
+       /* Check count is within bounds */
+       if (count > SDW_INTEL_MAX_LINKS) {
+               dev_err(&adev->dev, "Link count %d exceeds max %d\n",
+                       count, SDW_INTEL_MAX_LINKS);
+               return -EINVAL;
+       }
+
+       if (!count) {
+               dev_warn(&adev->dev, "No SoundWire links detected\n");
+               return -EINVAL;
+       }
+       dev_dbg(&adev->dev, "ACPI reports %d SDW Link devices\n", count);
+
+       info->count = count;
+       info->link_mask = 0;
+
+       for_each_set_bit(i, &list, SDW_INTEL_MAX_LINKS) {
+               if (ctrl_link_mask && !(ctrl_link_mask & BIT(i))) {
+                       dev_dbg(&adev->dev,
+                               "Link %d masked, will not be enabled\n", i);
+                       continue;
+               }
+
+               if (!is_link_enabled(fwnode, i)) {
+                       dev_dbg(&adev->dev,
+                               "Link %d not selected in firmware\n", i);
+                       continue;
+               }
+
+               info->link_mask |= BIT(i);
+       }
+
+       return 0;
+}
+
+static acpi_status sdw_intel_acpi_cb(acpi_handle handle, u32 level,
+                                    void *cdata, void **return_value)
+{
+       struct sdw_intel_acpi_info *info = cdata;
+       u64 adr;
+       int ret;
+
+       ret = acpi_get_local_u64_address(handle, &adr);
+       if (ret < 0)
+               return AE_OK; /* keep going */
+
+       if (!acpi_fetch_acpi_dev(handle)) {
+               pr_err("%s: Couldn't find ACPI handle\n", __func__);
+               return AE_NOT_FOUND;
+       }
+
+       /*
+        * On some Intel platforms, multiple children of the HDAS
+        * device can be found, but only one of them is the SoundWire
+        * controller. The SNDW device is always exposed with
+        * Name(_ADR, 0x40000000), with bits 31..28 representing the
+        * SoundWire link so filter accordingly
+        */
+       if (FIELD_GET(GENMASK(31, 28), adr) != SDW_LINK_TYPE)
+               return AE_OK; /* keep going */
+
+       if (adr != ctrl_addr)
+               return AE_OK; /* keep going */
+
+       /* found the correct SoundWire controller */
+       info->handle = handle;
+
+       /* device found, stop namespace walk */
+       return AE_CTRL_TERMINATE;
+}
+
+/**
+ * sdw_intel_acpi_scan() - SoundWire Intel init routine
+ * @parent_handle: ACPI parent handle
+ * @info: description of what firmware/DSDT tables expose
+ *
+ * This scans the namespace and queries firmware to figure out which
+ * links to enable. A follow-up use of sdw_intel_probe() and
+ * sdw_intel_startup() is required for creation of devices and bus
+ * startup
+ */
+int sdw_intel_acpi_scan(acpi_handle parent_handle,
+                       struct sdw_intel_acpi_info *info)
+{
+       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, 2,
+                                    sdw_intel_acpi_cb,
+                                    NULL, info, NULL);
+       if (ACPI_FAILURE(status) || info->handle == NULL)
+               return -ENODEV;
+
+       return sdw_intel_scan_controller(info);
+}
+EXPORT_SYMBOL_NS(sdw_intel_acpi_scan, "SND_INTEL_SOUNDWIRE_ACPI");
+
+MODULE_LICENSE("Dual BSD/GPL");
+MODULE_DESCRIPTION("Intel Soundwire ACPI helpers");
diff --git a/sound/hda/core/local.h b/sound/hda/core/local.h
new file mode 100644 (file)
index 0000000..5f03b20
--- /dev/null
@@ -0,0 +1,25 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Local helper macros and functions for HD-audio core drivers
+ */
+
+#ifndef __HDAC_LOCAL_H
+#define __HDAC_LOCAL_H
+
+extern const struct attribute_group *hdac_dev_attr_groups[];
+int hda_widget_sysfs_init(struct hdac_device *codec);
+int hda_widget_sysfs_reinit(struct hdac_device *codec, hda_nid_t start_nid,
+                           int num_nodes);
+void hda_widget_sysfs_exit(struct hdac_device *codec);
+
+int snd_hdac_bus_add_device(struct hdac_bus *bus, struct hdac_device *codec);
+void snd_hdac_bus_remove_device(struct hdac_bus *bus,
+                               struct hdac_device *codec);
+void snd_hdac_bus_queue_event(struct hdac_bus *bus, u32 res, u32 res_ex);
+int snd_hdac_bus_exec_verb(struct hdac_bus *bus, unsigned int addr,
+                          unsigned int cmd, unsigned int *res);
+
+int snd_hdac_exec_verb(struct hdac_device *codec, unsigned int cmd,
+                      unsigned int flags, unsigned int *res);
+
+#endif /* __HDAC_LOCAL_H */
diff --git a/sound/hda/core/regmap.c b/sound/hda/core/regmap.c
new file mode 100644 (file)
index 0000000..97cee09
--- /dev/null
@@ -0,0 +1,601 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Regmap support for HD-audio verbs
+ *
+ * A virtual register is translated to one or more hda verbs for write,
+ * vice versa for read.
+ *
+ * A few limitations:
+ * - Provided for not all verbs but only subset standard non-volatile verbs.
+ * - For reading, only AC_VERB_GET_* variants can be used.
+ * - For writing, mapped to the *corresponding* AC_VERB_SET_* variants,
+ *   so can't handle asymmetric verbs for read and write
+ */
+
+#include <linux/slab.h>
+#include <linux/device.h>
+#include <linux/regmap.h>
+#include <linux/export.h>
+#include <linux/pm.h>
+#include <sound/core.h>
+#include <sound/hdaudio.h>
+#include <sound/hda_regmap.h>
+#include "local.h"
+
+static int codec_pm_lock(struct hdac_device *codec)
+{
+       return snd_hdac_keep_power_up(codec);
+}
+
+static void codec_pm_unlock(struct hdac_device *codec, int lock)
+{
+       if (lock == 1)
+               snd_hdac_power_down_pm(codec);
+}
+
+#define get_verb(reg)  (((reg) >> 8) & 0xfff)
+
+static bool hda_volatile_reg(struct device *dev, unsigned int reg)
+{
+       struct hdac_device *codec = dev_to_hdac_dev(dev);
+       unsigned int verb = get_verb(reg);
+
+       switch (verb) {
+       case AC_VERB_GET_PROC_COEF:
+               return !codec->cache_coef;
+       case AC_VERB_GET_COEF_INDEX:
+       case AC_VERB_GET_PROC_STATE:
+       case AC_VERB_GET_POWER_STATE:
+       case AC_VERB_GET_PIN_SENSE:
+       case AC_VERB_GET_HDMI_DIP_SIZE:
+       case AC_VERB_GET_HDMI_ELDD:
+       case AC_VERB_GET_HDMI_DIP_INDEX:
+       case AC_VERB_GET_HDMI_DIP_DATA:
+       case AC_VERB_GET_HDMI_DIP_XMIT:
+       case AC_VERB_GET_HDMI_CP_CTRL:
+       case AC_VERB_GET_HDMI_CHAN_SLOT:
+       case AC_VERB_GET_DEVICE_SEL:
+       case AC_VERB_GET_DEVICE_LIST:   /* read-only volatile */
+               return true;
+       }
+
+       return false;
+}
+
+static bool hda_writeable_reg(struct device *dev, unsigned int reg)
+{
+       struct hdac_device *codec = dev_to_hdac_dev(dev);
+       unsigned int verb = get_verb(reg);
+       const unsigned int *v;
+       int i;
+
+       snd_array_for_each(&codec->vendor_verbs, i, v) {
+               if (verb == *v)
+                       return true;
+       }
+
+       if (codec->caps_overwriting)
+               return true;
+
+       switch (verb & 0xf00) {
+       case AC_VERB_GET_STREAM_FORMAT:
+       case AC_VERB_GET_AMP_GAIN_MUTE:
+               return true;
+       case AC_VERB_GET_PROC_COEF:
+               return codec->cache_coef;
+       case 0xf00:
+               break;
+       default:
+               return false;
+       }
+
+       switch (verb) {
+       case AC_VERB_GET_CONNECT_SEL:
+       case AC_VERB_GET_SDI_SELECT:
+       case AC_VERB_GET_PIN_WIDGET_CONTROL:
+       case AC_VERB_GET_UNSOLICITED_RESPONSE: /* only as SET_UNSOLICITED_ENABLE */
+       case AC_VERB_GET_BEEP_CONTROL:
+       case AC_VERB_GET_EAPD_BTLENABLE:
+       case AC_VERB_GET_DIGI_CONVERT_1:
+       case AC_VERB_GET_DIGI_CONVERT_2: /* only for beep control */
+       case AC_VERB_GET_VOLUME_KNOB_CONTROL:
+       case AC_VERB_GET_GPIO_MASK:
+       case AC_VERB_GET_GPIO_DIRECTION:
+       case AC_VERB_GET_GPIO_DATA: /* not for volatile read */
+       case AC_VERB_GET_GPIO_WAKE_MASK:
+       case AC_VERB_GET_GPIO_UNSOLICITED_RSP_MASK:
+       case AC_VERB_GET_GPIO_STICKY_MASK:
+               return true;
+       }
+
+       return false;
+}
+
+static bool hda_readable_reg(struct device *dev, unsigned int reg)
+{
+       struct hdac_device *codec = dev_to_hdac_dev(dev);
+       unsigned int verb = get_verb(reg);
+
+       if (codec->caps_overwriting)
+               return true;
+
+       switch (verb) {
+       case AC_VERB_PARAMETERS:
+       case AC_VERB_GET_CONNECT_LIST:
+       case AC_VERB_GET_SUBSYSTEM_ID:
+               return true;
+       /* below are basically writable, but disabled for reducing unnecessary
+        * writes at sync
+        */
+       case AC_VERB_GET_CONFIG_DEFAULT: /* usually just read */
+       case AC_VERB_GET_CONV: /* managed in PCM code */
+       case AC_VERB_GET_CVT_CHAN_COUNT: /* managed in HDMI CA code */
+               return true;
+       }
+
+       return hda_writeable_reg(dev, reg);
+}
+
+/*
+ * Stereo amp pseudo register:
+ * for making easier to handle the stereo volume control, we provide a
+ * fake register to deal both left and right channels by a single
+ * (pseudo) register access.  A verb consisting of SET_AMP_GAIN with
+ * *both* SET_LEFT and SET_RIGHT bits takes a 16bit value, the lower 8bit
+ * for the left and the upper 8bit for the right channel.
+ */
+static bool is_stereo_amp_verb(unsigned int reg)
+{
+       if (((reg >> 8) & 0x700) != AC_VERB_SET_AMP_GAIN_MUTE)
+               return false;
+       return (reg & (AC_AMP_SET_LEFT | AC_AMP_SET_RIGHT)) ==
+               (AC_AMP_SET_LEFT | AC_AMP_SET_RIGHT);
+}
+
+/* read a pseudo stereo amp register (16bit left+right) */
+static int hda_reg_read_stereo_amp(struct hdac_device *codec,
+                                  unsigned int reg, unsigned int *val)
+{
+       unsigned int left, right;
+       int err;
+
+       reg &= ~(AC_AMP_SET_LEFT | AC_AMP_SET_RIGHT);
+       err = snd_hdac_exec_verb(codec, reg | AC_AMP_GET_LEFT, 0, &left);
+       if (err < 0)
+               return err;
+       err = snd_hdac_exec_verb(codec, reg | AC_AMP_GET_RIGHT, 0, &right);
+       if (err < 0)
+               return err;
+       *val = left | (right << 8);
+       return 0;
+}
+
+/* write a pseudo stereo amp register (16bit left+right) */
+static int hda_reg_write_stereo_amp(struct hdac_device *codec,
+                                   unsigned int reg, unsigned int val)
+{
+       int err;
+       unsigned int verb, left, right;
+
+       verb = AC_VERB_SET_AMP_GAIN_MUTE << 8;
+       if (reg & AC_AMP_GET_OUTPUT)
+               verb |= AC_AMP_SET_OUTPUT;
+       else
+               verb |= AC_AMP_SET_INPUT | ((reg & 0xf) << 8);
+       reg = (reg & ~0xfffff) | verb;
+
+       left = val & 0xff;
+       right = (val >> 8) & 0xff;
+       if (left == right) {
+               reg |= AC_AMP_SET_LEFT | AC_AMP_SET_RIGHT;
+               return snd_hdac_exec_verb(codec, reg | left, 0, NULL);
+       }
+
+       err = snd_hdac_exec_verb(codec, reg | AC_AMP_SET_LEFT | left, 0, NULL);
+       if (err < 0)
+               return err;
+       err = snd_hdac_exec_verb(codec, reg | AC_AMP_SET_RIGHT | right, 0, NULL);
+       if (err < 0)
+               return err;
+       return 0;
+}
+
+/* read a pseudo coef register (16bit) */
+static int hda_reg_read_coef(struct hdac_device *codec, unsigned int reg,
+                            unsigned int *val)
+{
+       unsigned int verb;
+       int err;
+
+       if (!codec->cache_coef)
+               return -EINVAL;
+       /* LSB 8bit = coef index */
+       verb = (reg & ~0xfff00) | (AC_VERB_SET_COEF_INDEX << 8);
+       err = snd_hdac_exec_verb(codec, verb, 0, NULL);
+       if (err < 0)
+               return err;
+       verb = (reg & ~0xfffff) | (AC_VERB_GET_COEF_INDEX << 8);
+       return snd_hdac_exec_verb(codec, verb, 0, val);
+}
+
+/* write a pseudo coef register (16bit) */
+static int hda_reg_write_coef(struct hdac_device *codec, unsigned int reg,
+                             unsigned int val)
+{
+       unsigned int verb;
+       int err;
+
+       if (!codec->cache_coef)
+               return -EINVAL;
+       /* LSB 8bit = coef index */
+       verb = (reg & ~0xfff00) | (AC_VERB_SET_COEF_INDEX << 8);
+       err = snd_hdac_exec_verb(codec, verb, 0, NULL);
+       if (err < 0)
+               return err;
+       verb = (reg & ~0xfffff) | (AC_VERB_GET_COEF_INDEX << 8) |
+               (val & 0xffff);
+       return snd_hdac_exec_verb(codec, verb, 0, NULL);
+}
+
+static int hda_reg_read(void *context, unsigned int reg, unsigned int *val)
+{
+       struct hdac_device *codec = context;
+       int verb = get_verb(reg);
+       int err;
+       int pm_lock = 0;
+
+       if (verb != AC_VERB_GET_POWER_STATE) {
+               pm_lock = codec_pm_lock(codec);
+               if (pm_lock < 0)
+                       return -EAGAIN;
+       }
+       reg |= (codec->addr << 28);
+       if (is_stereo_amp_verb(reg)) {
+               err = hda_reg_read_stereo_amp(codec, reg, val);
+               goto out;
+       }
+       if (verb == AC_VERB_GET_PROC_COEF) {
+               err = hda_reg_read_coef(codec, reg, val);
+               goto out;
+       }
+       if ((verb & 0x700) == AC_VERB_SET_AMP_GAIN_MUTE)
+               reg &= ~AC_AMP_FAKE_MUTE;
+
+       err = snd_hdac_exec_verb(codec, reg, 0, val);
+       if (err < 0)
+               goto out;
+       /* special handling for asymmetric reads */
+       if (verb == AC_VERB_GET_POWER_STATE) {
+               if (*val & AC_PWRST_ERROR)
+                       *val = -1;
+               else /* take only the actual state */
+                       *val = (*val >> 4) & 0x0f;
+       }
+ out:
+       codec_pm_unlock(codec, pm_lock);
+       return err;
+}
+
+static int hda_reg_write(void *context, unsigned int reg, unsigned int val)
+{
+       struct hdac_device *codec = context;
+       unsigned int verb;
+       int i, bytes, err;
+       int pm_lock = 0;
+
+       if (codec->caps_overwriting)
+               return 0;
+
+       reg &= ~0x00080000U; /* drop GET bit */
+       reg |= (codec->addr << 28);
+       verb = get_verb(reg);
+
+       if (verb != AC_VERB_SET_POWER_STATE) {
+               pm_lock = codec_pm_lock(codec);
+               if (pm_lock < 0)
+                       return codec->lazy_cache ? 0 : -EAGAIN;
+       }
+
+       if (is_stereo_amp_verb(reg)) {
+               err = hda_reg_write_stereo_amp(codec, reg, val);
+               goto out;
+       }
+
+       if (verb == AC_VERB_SET_PROC_COEF) {
+               err = hda_reg_write_coef(codec, reg, val);
+               goto out;
+       }
+
+       switch (verb & 0xf00) {
+       case AC_VERB_SET_AMP_GAIN_MUTE:
+               if ((reg & AC_AMP_FAKE_MUTE) && (val & AC_AMP_MUTE))
+                       val = 0;
+               verb = AC_VERB_SET_AMP_GAIN_MUTE;
+               if (reg & AC_AMP_GET_LEFT)
+                       verb |= AC_AMP_SET_LEFT >> 8;
+               else
+                       verb |= AC_AMP_SET_RIGHT >> 8;
+               if (reg & AC_AMP_GET_OUTPUT) {
+                       verb |= AC_AMP_SET_OUTPUT >> 8;
+               } else {
+                       verb |= AC_AMP_SET_INPUT >> 8;
+                       verb |= reg & 0xf;
+               }
+               break;
+       }
+
+       switch (verb) {
+       case AC_VERB_SET_DIGI_CONVERT_1:
+               bytes = 2;
+               break;
+       case AC_VERB_SET_CONFIG_DEFAULT_BYTES_0:
+               bytes = 4;
+               break;
+       default:
+               bytes = 1;
+               break;
+       }
+
+       for (i = 0; i < bytes; i++) {
+               reg &= ~0xfffff;
+               reg |= (verb + i) << 8 | ((val >> (8 * i)) & 0xff);
+               err = snd_hdac_exec_verb(codec, reg, 0, NULL);
+               if (err < 0)
+                       goto out;
+       }
+
+ out:
+       codec_pm_unlock(codec, pm_lock);
+       return err;
+}
+
+static const struct regmap_config hda_regmap_cfg = {
+       .name = "hdaudio",
+       .reg_bits = 32,
+       .val_bits = 32,
+       .max_register = 0xfffffff,
+       .writeable_reg = hda_writeable_reg,
+       .readable_reg = hda_readable_reg,
+       .volatile_reg = hda_volatile_reg,
+       .cache_type = REGCACHE_MAPLE,
+       .reg_read = hda_reg_read,
+       .reg_write = hda_reg_write,
+       .use_single_read = true,
+       .use_single_write = true,
+       .disable_locking = true,
+};
+
+/**
+ * snd_hdac_regmap_init - Initialize regmap for HDA register accesses
+ * @codec: the codec object
+ *
+ * Returns zero for success or a negative error code.
+ */
+int snd_hdac_regmap_init(struct hdac_device *codec)
+{
+       struct regmap *regmap;
+
+       regmap = regmap_init(&codec->dev, NULL, codec, &hda_regmap_cfg);
+       if (IS_ERR(regmap))
+               return PTR_ERR(regmap);
+       codec->regmap = regmap;
+       snd_array_init(&codec->vendor_verbs, sizeof(unsigned int), 8);
+       return 0;
+}
+EXPORT_SYMBOL_GPL(snd_hdac_regmap_init);
+
+/**
+ * snd_hdac_regmap_exit - Release the regmap from HDA codec
+ * @codec: the codec object
+ */
+void snd_hdac_regmap_exit(struct hdac_device *codec)
+{
+       if (codec->regmap) {
+               regmap_exit(codec->regmap);
+               codec->regmap = NULL;
+               snd_array_free(&codec->vendor_verbs);
+       }
+}
+EXPORT_SYMBOL_GPL(snd_hdac_regmap_exit);
+
+/**
+ * snd_hdac_regmap_add_vendor_verb - add a vendor-specific verb to regmap
+ * @codec: the codec object
+ * @verb: verb to allow accessing via regmap
+ *
+ * Returns zero for success or a negative error code.
+ */
+int snd_hdac_regmap_add_vendor_verb(struct hdac_device *codec,
+                                   unsigned int verb)
+{
+       unsigned int *p = snd_array_new(&codec->vendor_verbs);
+
+       if (!p)
+               return -ENOMEM;
+       *p = verb | 0x800; /* set GET bit */
+       return 0;
+}
+EXPORT_SYMBOL_GPL(snd_hdac_regmap_add_vendor_verb);
+
+/*
+ * helper functions
+ */
+
+/* write a pseudo-register value (w/o power sequence) */
+static int reg_raw_write(struct hdac_device *codec, unsigned int reg,
+                        unsigned int val)
+{
+       int err;
+
+       mutex_lock(&codec->regmap_lock);
+       if (!codec->regmap)
+               err = hda_reg_write(codec, reg, val);
+       else
+               err = regmap_write(codec->regmap, reg, val);
+       mutex_unlock(&codec->regmap_lock);
+       return err;
+}
+
+/* a helper macro to call @func_call; retry with power-up if failed */
+#define CALL_RAW_FUNC(codec, func_call)                                \
+       ({                                                      \
+               int _err = func_call;                           \
+               if (_err == -EAGAIN) {                          \
+                       _err = snd_hdac_power_up_pm(codec);     \
+                       if (_err >= 0)                          \
+                               _err = func_call;               \
+                       snd_hdac_power_down_pm(codec);          \
+               }                                               \
+               _err;})
+
+/**
+ * snd_hdac_regmap_write_raw - write a pseudo register with power mgmt
+ * @codec: the codec object
+ * @reg: pseudo register
+ * @val: value to write
+ *
+ * Returns zero if successful or a negative error code.
+ */
+int snd_hdac_regmap_write_raw(struct hdac_device *codec, unsigned int reg,
+                             unsigned int val)
+{
+       return CALL_RAW_FUNC(codec, reg_raw_write(codec, reg, val));
+}
+EXPORT_SYMBOL_GPL(snd_hdac_regmap_write_raw);
+
+static int reg_raw_read(struct hdac_device *codec, unsigned int reg,
+                       unsigned int *val, bool uncached)
+{
+       int err;
+
+       mutex_lock(&codec->regmap_lock);
+       if (uncached || !codec->regmap)
+               err = hda_reg_read(codec, reg, val);
+       else
+               err = regmap_read(codec->regmap, reg, val);
+       mutex_unlock(&codec->regmap_lock);
+       return err;
+}
+
+static int __snd_hdac_regmap_read_raw(struct hdac_device *codec,
+                                     unsigned int reg, unsigned int *val,
+                                     bool uncached)
+{
+       return CALL_RAW_FUNC(codec, reg_raw_read(codec, reg, val, uncached));
+}
+
+/**
+ * snd_hdac_regmap_read_raw - read a pseudo register with power mgmt
+ * @codec: the codec object
+ * @reg: pseudo register
+ * @val: pointer to store the read value
+ *
+ * Returns zero if successful or a negative error code.
+ */
+int snd_hdac_regmap_read_raw(struct hdac_device *codec, unsigned int reg,
+                            unsigned int *val)
+{
+       return __snd_hdac_regmap_read_raw(codec, reg, val, false);
+}
+EXPORT_SYMBOL_GPL(snd_hdac_regmap_read_raw);
+
+/* Works like snd_hdac_regmap_read_raw(), but this doesn't read from the
+ * cache but always via hda verbs.
+ */
+int snd_hdac_regmap_read_raw_uncached(struct hdac_device *codec,
+                                     unsigned int reg, unsigned int *val)
+{
+       return __snd_hdac_regmap_read_raw(codec, reg, val, true);
+}
+
+static int reg_raw_update(struct hdac_device *codec, unsigned int reg,
+                         unsigned int mask, unsigned int val)
+{
+       unsigned int orig;
+       bool change;
+       int err;
+
+       mutex_lock(&codec->regmap_lock);
+       if (codec->regmap) {
+               err = regmap_update_bits_check(codec->regmap, reg, mask, val,
+                                              &change);
+               if (!err)
+                       err = change ? 1 : 0;
+       } else {
+               err = hda_reg_read(codec, reg, &orig);
+               if (!err) {
+                       val &= mask;
+                       val |= orig & ~mask;
+                       if (val != orig) {
+                               err = hda_reg_write(codec, reg, val);
+                               if (!err)
+                                       err = 1;
+                       }
+               }
+       }
+       mutex_unlock(&codec->regmap_lock);
+       return err;
+}
+
+/**
+ * snd_hdac_regmap_update_raw - update a pseudo register with power mgmt
+ * @codec: the codec object
+ * @reg: pseudo register
+ * @mask: bit mask to update
+ * @val: value to update
+ *
+ * Returns zero if successful or a negative error code.
+ */
+int snd_hdac_regmap_update_raw(struct hdac_device *codec, unsigned int reg,
+                              unsigned int mask, unsigned int val)
+{
+       return CALL_RAW_FUNC(codec, reg_raw_update(codec, reg, mask, val));
+}
+EXPORT_SYMBOL_GPL(snd_hdac_regmap_update_raw);
+
+static int reg_raw_update_once(struct hdac_device *codec, unsigned int reg,
+                              unsigned int mask, unsigned int val)
+{
+       int err = 0;
+
+       if (!codec->regmap)
+               return reg_raw_update(codec, reg, mask, val);
+
+       mutex_lock(&codec->regmap_lock);
+       /* Discard any updates to already initialised registers. */
+       if (!regcache_reg_cached(codec->regmap, reg))
+               err = regmap_update_bits(codec->regmap, reg, mask, val);
+       mutex_unlock(&codec->regmap_lock);
+       return err;
+}
+
+/**
+ * snd_hdac_regmap_update_raw_once - initialize the register value only once
+ * @codec: the codec object
+ * @reg: pseudo register
+ * @mask: bit mask to update
+ * @val: value to update
+ *
+ * Performs the update of the register bits only once when the register
+ * hasn't been initialized yet.  Used in HD-audio legacy driver.
+ * Returns zero if successful or a negative error code
+ */
+int snd_hdac_regmap_update_raw_once(struct hdac_device *codec, unsigned int reg,
+                                   unsigned int mask, unsigned int val)
+{
+       return CALL_RAW_FUNC(codec, reg_raw_update_once(codec, reg, mask, val));
+}
+EXPORT_SYMBOL_GPL(snd_hdac_regmap_update_raw_once);
+
+/**
+ * snd_hdac_regmap_sync - sync out the cached values for PM resume
+ * @codec: the codec object
+ */
+void snd_hdac_regmap_sync(struct hdac_device *codec)
+{
+       mutex_lock(&codec->regmap_lock);
+       if (codec->regmap)
+               regcache_sync(codec->regmap);
+       mutex_unlock(&codec->regmap_lock);
+}
+EXPORT_SYMBOL_GPL(snd_hdac_regmap_sync);
diff --git a/sound/hda/core/stream.c b/sound/hda/core/stream.c
new file mode 100644 (file)
index 0000000..e7f6208
--- /dev/null
@@ -0,0 +1,1014 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * HD-audio stream operations
+ */
+
+#include <linux/kernel.h>
+#include <linux/delay.h>
+#include <linux/export.h>
+#include <linux/clocksource.h>
+#include <sound/compress_driver.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/hdaudio.h>
+#include <sound/hda_register.h>
+#include "trace.h"
+
+/*
+ * the hdac_stream library is intended to be used with the following
+ * transitions. The states are not formally defined in the code but loosely
+ * inspired by boolean variables. Note that the 'prepared' field is not used
+ * in this library but by the callers during the hw_params/prepare transitions
+ *
+ *                        |
+ *     stream_init()      |
+ *                        v
+ *                     +--+-------+
+ *                     |  unused  |
+ *                     +--+----+--+
+ *                        |    ^
+ *     stream_assign()    |    |    stream_release()
+ *                        v    |
+ *                     +--+----+--+
+ *                     |  opened  |
+ *                     +--+----+--+
+ *                        |    ^
+ *     stream_reset()     |    |
+ *     stream_setup()     |    |    stream_cleanup()
+ *                        v    |
+ *                     +--+----+--+
+ *                     | prepared |
+ *                     +--+----+--+
+ *                        |    ^
+ *     stream_start()     |    |    stream_stop()
+ *                        v    |
+ *                     +--+----+--+
+ *                     |  running |
+ *                     +----------+
+ */
+
+/**
+ * snd_hdac_get_stream_stripe_ctl - get stripe control value
+ * @bus: HD-audio core bus
+ * @substream: PCM substream
+ */
+int snd_hdac_get_stream_stripe_ctl(struct hdac_bus *bus,
+                                  struct snd_pcm_substream *substream)
+{
+       struct snd_pcm_runtime *runtime = substream->runtime;
+       unsigned int channels = runtime->channels,
+                    rate = runtime->rate,
+                    bits_per_sample = runtime->sample_bits,
+                    max_sdo_lines, value, sdo_line;
+
+       /* T_AZA_GCAP_NSDO is 1:2 bitfields in GCAP */
+       max_sdo_lines = snd_hdac_chip_readl(bus, GCAP) & AZX_GCAP_NSDO;
+
+       /* following is from HD audio spec */
+       for (sdo_line = max_sdo_lines; sdo_line > 0; sdo_line >>= 1) {
+               if (rate > 48000)
+                       value = (channels * bits_per_sample *
+                                       (rate / 48000)) / sdo_line;
+               else
+                       value = (channels * bits_per_sample) / sdo_line;
+
+               if (value >= bus->sdo_limit)
+                       break;
+       }
+
+       /* stripe value: 0 for 1SDO, 1 for 2SDO, 2 for 4SDO lines */
+       return sdo_line >> 1;
+}
+EXPORT_SYMBOL_GPL(snd_hdac_get_stream_stripe_ctl);
+
+/**
+ * snd_hdac_stream_init - initialize each stream (aka device)
+ * @bus: HD-audio core bus
+ * @azx_dev: HD-audio 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
+ *
+ * Assign the starting bdl address to each stream (device) and initialize.
+ */
+void snd_hdac_stream_init(struct hdac_bus *bus, struct hdac_stream *azx_dev,
+                         int idx, int direction, int tag)
+{
+       azx_dev->bus = bus;
+       /* offset: SDI0=0x80, SDI1=0xa0, ... SDO3=0x160 */
+       azx_dev->sd_addr = bus->remap_addr + (0x20 * idx + 0x80);
+       /* int mask: SDI0=0x01, SDI1=0x02, ... SDO3=0x80 */
+       azx_dev->sd_int_sta_mask = 1 << idx;
+       azx_dev->index = idx;
+       azx_dev->direction = direction;
+       azx_dev->stream_tag = tag;
+       snd_hdac_dsp_lock_init(azx_dev);
+       list_add_tail(&azx_dev->list, &bus->stream_list);
+
+       if (bus->spbcap) {
+               azx_dev->spib_addr = bus->spbcap + AZX_SPB_BASE +
+                                       AZX_SPB_INTERVAL * idx +
+                                       AZX_SPB_SPIB;
+
+               azx_dev->fifo_addr = bus->spbcap + AZX_SPB_BASE +
+                                       AZX_SPB_INTERVAL * idx +
+                                       AZX_SPB_MAXFIFO;
+       }
+
+       if (bus->drsmcap)
+               azx_dev->dpibr_addr = bus->drsmcap + AZX_DRSM_BASE +
+                                       AZX_DRSM_INTERVAL * idx;
+}
+EXPORT_SYMBOL_GPL(snd_hdac_stream_init);
+
+/**
+ * snd_hdac_stream_start - start a stream
+ * @azx_dev: HD-audio core stream to start
+ *
+ * Start a stream, set start_wallclk and set the running flag.
+ */
+void snd_hdac_stream_start(struct hdac_stream *azx_dev)
+{
+       struct hdac_bus *bus = azx_dev->bus;
+       int stripe_ctl;
+
+       trace_snd_hdac_stream_start(bus, azx_dev);
+
+       azx_dev->start_wallclk = snd_hdac_chip_readl(bus, WALLCLK);
+
+       /* enable SIE */
+       snd_hdac_chip_updatel(bus, INTCTL,
+                             1 << azx_dev->index,
+                             1 << azx_dev->index);
+       /* set stripe control */
+       if (azx_dev->stripe) {
+               if (azx_dev->substream)
+                       stripe_ctl = snd_hdac_get_stream_stripe_ctl(bus, azx_dev->substream);
+               else
+                       stripe_ctl = 0;
+               snd_hdac_stream_updateb(azx_dev, SD_CTL_3B, SD_CTL_STRIPE_MASK,
+                                       stripe_ctl);
+       }
+       /* set DMA start and interrupt mask */
+       if (bus->access_sdnctl_in_dword)
+               snd_hdac_stream_updatel(azx_dev, SD_CTL,
+                               0, SD_CTL_DMA_START | SD_INT_MASK);
+       else
+               snd_hdac_stream_updateb(azx_dev, SD_CTL,
+                               0, SD_CTL_DMA_START | SD_INT_MASK);
+       azx_dev->running = true;
+}
+EXPORT_SYMBOL_GPL(snd_hdac_stream_start);
+
+/**
+ * snd_hdac_stream_clear - helper to clear stream registers and stop DMA transfers
+ * @azx_dev: HD-audio core stream to stop
+ */
+static void snd_hdac_stream_clear(struct hdac_stream *azx_dev)
+{
+       snd_hdac_stream_updateb(azx_dev, SD_CTL,
+                               SD_CTL_DMA_START | SD_INT_MASK, 0);
+       snd_hdac_stream_writeb(azx_dev, SD_STS, SD_INT_MASK); /* to be sure */
+       if (azx_dev->stripe)
+               snd_hdac_stream_updateb(azx_dev, SD_CTL_3B, SD_CTL_STRIPE_MASK, 0);
+       azx_dev->running = false;
+}
+
+/**
+ * snd_hdac_stream_stop - stop a stream
+ * @azx_dev: HD-audio core stream to stop
+ *
+ * Stop a stream DMA and disable stream interrupt
+ */
+void snd_hdac_stream_stop(struct hdac_stream *azx_dev)
+{
+       trace_snd_hdac_stream_stop(azx_dev->bus, azx_dev);
+
+       snd_hdac_stream_clear(azx_dev);
+       /* disable SIE */
+       snd_hdac_chip_updatel(azx_dev->bus, INTCTL, 1 << azx_dev->index, 0);
+}
+EXPORT_SYMBOL_GPL(snd_hdac_stream_stop);
+
+/**
+ * snd_hdac_stop_streams - stop all streams
+ * @bus: HD-audio core bus
+ */
+void snd_hdac_stop_streams(struct hdac_bus *bus)
+{
+       struct hdac_stream *stream;
+
+       list_for_each_entry(stream, &bus->stream_list, list)
+               snd_hdac_stream_stop(stream);
+}
+EXPORT_SYMBOL_GPL(snd_hdac_stop_streams);
+
+/**
+ * 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)
+{
+
+       if (bus->chip_init) {
+               snd_hdac_stop_streams(bus);
+               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
+ */
+void snd_hdac_stream_reset(struct hdac_stream *azx_dev)
+{
+       unsigned char val;
+       int dma_run_state;
+
+       snd_hdac_stream_clear(azx_dev);
+
+       dma_run_state = snd_hdac_stream_readb(azx_dev, SD_CTL) & SD_CTL_DMA_START;
+
+       snd_hdac_stream_updateb(azx_dev, SD_CTL, 0, SD_CTL_STREAM_RESET);
+
+       /* wait for hardware to report that the stream entered reset */
+       snd_hdac_stream_readb_poll(azx_dev, SD_CTL, val, (val & SD_CTL_STREAM_RESET), 3, 300);
+
+       if (azx_dev->bus->dma_stop_delay && dma_run_state)
+               udelay(azx_dev->bus->dma_stop_delay);
+
+       snd_hdac_stream_updateb(azx_dev, SD_CTL, SD_CTL_STREAM_RESET, 0);
+
+       /* wait for hardware to report that the stream is out of reset */
+       snd_hdac_stream_readb_poll(azx_dev, SD_CTL, val, !(val & SD_CTL_STREAM_RESET), 3, 300);
+
+       /* reset first position - may not be synced with hw at this time */
+       if (azx_dev->posbuf)
+               *azx_dev->posbuf = 0;
+}
+EXPORT_SYMBOL_GPL(snd_hdac_stream_reset);
+
+/**
+ * snd_hdac_stream_setup -  set up the SD for streaming
+ * @azx_dev: HD-audio core stream to set up
+ * @code_loading: Whether the stream is for PCM or code-loading.
+ */
+int snd_hdac_stream_setup(struct hdac_stream *azx_dev, bool code_loading)
+{
+       struct hdac_bus *bus = azx_dev->bus;
+       struct snd_pcm_runtime *runtime;
+       unsigned int val;
+       u16 reg;
+       int ret;
+
+       if (azx_dev->substream)
+               runtime = azx_dev->substream->runtime;
+       else
+               runtime = NULL;
+       /* make sure the run bit is zero for SD */
+       snd_hdac_stream_clear(azx_dev);
+       /* program the stream_tag */
+       val = snd_hdac_stream_readl(azx_dev, SD_CTL);
+       val = (val & ~SD_CTL_STREAM_TAG_MASK) |
+               (azx_dev->stream_tag << SD_CTL_STREAM_TAG_SHIFT);
+       if (!bus->snoop)
+               val |= SD_CTL_TRAFFIC_PRIO;
+       snd_hdac_stream_writel(azx_dev, SD_CTL, val);
+
+       /* program the length of samples in cyclic buffer */
+       snd_hdac_stream_writel(azx_dev, SD_CBL, azx_dev->bufsize);
+
+       /* program the stream format */
+       /* this value needs to be the same as the one programmed */
+       snd_hdac_stream_writew(azx_dev, SD_FORMAT, azx_dev->format_val);
+
+       /* program the stream LVI (last valid index) of the BDL */
+       snd_hdac_stream_writew(azx_dev, SD_LVI, azx_dev->frags - 1);
+
+       /* program the BDL address */
+       /* lower BDL address */
+       snd_hdac_stream_writel(azx_dev, SD_BDLPL, (u32)azx_dev->bdl.addr);
+       /* upper BDL address */
+       snd_hdac_stream_writel(azx_dev, SD_BDLPU,
+                              upper_32_bits(azx_dev->bdl.addr));
+
+       /* enable the position buffer */
+       if (bus->use_posbuf && bus->posbuf.addr) {
+               if (!(snd_hdac_chip_readl(bus, DPLBASE) & AZX_DPLBASE_ENABLE))
+                       snd_hdac_chip_writel(bus, DPLBASE,
+                               (u32)bus->posbuf.addr | AZX_DPLBASE_ENABLE);
+       }
+
+       /* set the interrupt enable bits in the descriptor control register */
+       snd_hdac_stream_updatel(azx_dev, SD_CTL, 0, SD_INT_MASK);
+
+       if (!code_loading) {
+               /* Once SDxFMT is set, the controller programs SDxFIFOS to non-zero value. */
+               ret = snd_hdac_stream_readw_poll(azx_dev, SD_FIFOSIZE, reg,
+                                                reg & AZX_SD_FIFOSIZE_MASK, 3, 300);
+               if (ret)
+                       dev_dbg(bus->dev, "polling SD_FIFOSIZE 0x%04x failed: %d\n",
+                               AZX_REG_SD_FIFOSIZE, ret);
+               azx_dev->fifo_size = reg;
+       }
+
+       /* when LPIB delay correction gives a small negative value,
+        * we ignore it; currently set the threshold statically to
+        * 64 frames
+        */
+       if (runtime && runtime->period_size > 64)
+               azx_dev->delay_negative_threshold =
+                       -frames_to_bytes(runtime, 64);
+       else
+               azx_dev->delay_negative_threshold = 0;
+
+       /* wallclk has 24Mhz clock source */
+       if (runtime)
+               azx_dev->period_wallclk = (((runtime->period_size * 24000) /
+                                   runtime->rate) * 1000);
+
+       return 0;
+}
+EXPORT_SYMBOL_GPL(snd_hdac_stream_setup);
+
+/**
+ * snd_hdac_stream_cleanup - cleanup a stream
+ * @azx_dev: HD-audio core stream to clean up
+ */
+void snd_hdac_stream_cleanup(struct hdac_stream *azx_dev)
+{
+       snd_hdac_stream_writel(azx_dev, SD_BDLPL, 0);
+       snd_hdac_stream_writel(azx_dev, SD_BDLPU, 0);
+       snd_hdac_stream_writel(azx_dev, SD_CTL, 0);
+       azx_dev->bufsize = 0;
+       azx_dev->period_bytes = 0;
+       azx_dev->format_val = 0;
+}
+EXPORT_SYMBOL_GPL(snd_hdac_stream_cleanup);
+
+/**
+ * snd_hdac_stream_assign - assign a stream for the PCM
+ * @bus: HD-audio core bus
+ * @substream: PCM substream to assign
+ *
+ * Look for an unused stream for the given PCM substream, assign it
+ * and return the stream object.  If no stream is free, returns NULL.
+ * The function tries to keep using the same stream object when it's used
+ * beforehand.  Also, when bus->reverse_assign flag is set, the last free
+ * or matching entry is returned.  This is needed for some strange codecs.
+ */
+struct hdac_stream *snd_hdac_stream_assign(struct hdac_bus *bus,
+                                          struct snd_pcm_substream *substream)
+{
+       struct hdac_stream *azx_dev;
+       struct hdac_stream *res = NULL;
+
+       /* make a non-zero unique key for the substream */
+       int key = (substream->number << 2) | (substream->stream + 1);
+
+       if (substream->pcm)
+               key |= (substream->pcm->device << 16);
+
+       spin_lock_irq(&bus->reg_lock);
+       list_for_each_entry(azx_dev, &bus->stream_list, list) {
+               if (azx_dev->direction != substream->stream)
+                       continue;
+               if (azx_dev->opened)
+                       continue;
+               if (azx_dev->assigned_key == key) {
+                       res = azx_dev;
+                       break;
+               }
+               if (!res || bus->reverse_assign)
+                       res = azx_dev;
+       }
+       if (res) {
+               res->opened = 1;
+               res->running = 0;
+               res->assigned_key = key;
+               res->substream = substream;
+       }
+       spin_unlock_irq(&bus->reg_lock);
+       return res;
+}
+EXPORT_SYMBOL_GPL(snd_hdac_stream_assign);
+
+/**
+ * snd_hdac_stream_release_locked - release the assigned stream
+ * @azx_dev: HD-audio core stream to release
+ *
+ * Release the stream that has been assigned by snd_hdac_stream_assign().
+ * The bus->reg_lock needs to be taken at a higher level
+ */
+void snd_hdac_stream_release_locked(struct hdac_stream *azx_dev)
+{
+       azx_dev->opened = 0;
+       azx_dev->running = 0;
+       azx_dev->substream = NULL;
+}
+EXPORT_SYMBOL_GPL(snd_hdac_stream_release_locked);
+
+/**
+ * snd_hdac_stream_release - release the assigned stream
+ * @azx_dev: HD-audio core stream to release
+ *
+ * Release the stream that has been assigned by snd_hdac_stream_assign().
+ */
+void snd_hdac_stream_release(struct hdac_stream *azx_dev)
+{
+       struct hdac_bus *bus = azx_dev->bus;
+
+       spin_lock_irq(&bus->reg_lock);
+       snd_hdac_stream_release_locked(azx_dev);
+       spin_unlock_irq(&bus->reg_lock);
+}
+EXPORT_SYMBOL_GPL(snd_hdac_stream_release);
+
+/**
+ * snd_hdac_get_stream - return hdac_stream based on stream_tag and
+ * direction
+ *
+ * @bus: HD-audio core bus
+ * @dir: direction for the stream to be found
+ * @stream_tag: stream tag for stream to be found
+ */
+struct hdac_stream *snd_hdac_get_stream(struct hdac_bus *bus,
+                                       int dir, int stream_tag)
+{
+       struct hdac_stream *s;
+
+       list_for_each_entry(s, &bus->stream_list, list) {
+               if (s->direction == dir && s->stream_tag == stream_tag)
+                       return s;
+       }
+
+       return NULL;
+}
+EXPORT_SYMBOL_GPL(snd_hdac_get_stream);
+
+/*
+ * set up a BDL entry
+ */
+static int setup_bdle(struct hdac_bus *bus,
+                     struct snd_dma_buffer *dmab,
+                     struct hdac_stream *azx_dev, __le32 **bdlp,
+                     int ofs, int size, int with_ioc)
+{
+       __le32 *bdl = *bdlp;
+
+       while (size > 0) {
+               dma_addr_t addr;
+               int chunk;
+
+               if (azx_dev->frags >= AZX_MAX_BDL_ENTRIES)
+                       return -EINVAL;
+
+               addr = snd_sgbuf_get_addr(dmab, ofs);
+               /* program the address field of the BDL entry */
+               bdl[0] = cpu_to_le32((u32)addr);
+               bdl[1] = cpu_to_le32(upper_32_bits(addr));
+               /* program the size field of the BDL entry */
+               chunk = snd_sgbuf_get_chunk_size(dmab, ofs, size);
+               /* one BDLE cannot cross 4K boundary on CTHDA chips */
+               if (bus->align_bdle_4k) {
+                       u32 remain = 0x1000 - (ofs & 0xfff);
+
+                       if (chunk > remain)
+                               chunk = remain;
+               }
+               bdl[2] = cpu_to_le32(chunk);
+               /* program the IOC to enable interrupt
+                * only when the whole fragment is processed
+                */
+               size -= chunk;
+               bdl[3] = (size || !with_ioc) ? 0 : cpu_to_le32(0x01);
+               bdl += 4;
+               azx_dev->frags++;
+               ofs += chunk;
+       }
+       *bdlp = bdl;
+       return ofs;
+}
+
+/**
+ * snd_hdac_stream_setup_bdle - set up BDL entries
+ * @azx_dev: HD-audio core stream to set up
+ * @dmab: allocated DMA buffer
+ * @runtime: substream runtime, optional
+ *
+ * Set up the buffer descriptor table of the given stream based on the
+ * period and buffer sizes of the assigned PCM substream.
+ */
+static int snd_hdac_stream_setup_bdle(struct hdac_stream *azx_dev, struct snd_dma_buffer *dmab,
+                                     struct snd_pcm_runtime *runtime)
+{
+       struct hdac_bus *bus = azx_dev->bus;
+       int i, ofs, periods, period_bytes;
+       int pos_adj, pos_align;
+       __le32 *bdl;
+
+       /* reset BDL address */
+       snd_hdac_stream_writel(azx_dev, SD_BDLPL, 0);
+       snd_hdac_stream_writel(azx_dev, SD_BDLPU, 0);
+
+       period_bytes = azx_dev->period_bytes;
+       periods = azx_dev->bufsize / period_bytes;
+
+       /* program the initial BDL entries */
+       bdl = (__le32 *)azx_dev->bdl.area;
+       ofs = 0;
+       azx_dev->frags = 0;
+
+       pos_adj = bus->bdl_pos_adj;
+       if (runtime && !azx_dev->no_period_wakeup && pos_adj > 0) {
+               pos_align = pos_adj;
+               pos_adj = DIV_ROUND_UP(pos_adj * runtime->rate, 48000);
+               if (!pos_adj)
+                       pos_adj = pos_align;
+               else
+                       pos_adj = roundup(pos_adj, pos_align);
+               pos_adj = frames_to_bytes(runtime, pos_adj);
+               if (pos_adj >= period_bytes) {
+                       dev_warn(bus->dev, "Too big adjustment %d\n",
+                                pos_adj);
+                       pos_adj = 0;
+               } else {
+                       ofs = setup_bdle(bus, dmab, azx_dev,
+                                        &bdl, ofs, pos_adj, true);
+                       if (ofs < 0)
+                               goto error;
+               }
+       } else
+               pos_adj = 0;
+
+       for (i = 0; i < periods; i++) {
+               if (i == periods - 1 && pos_adj)
+                       ofs = setup_bdle(bus, dmab, azx_dev,
+                                        &bdl, ofs, period_bytes - pos_adj, 0);
+               else
+                       ofs = setup_bdle(bus, dmab, azx_dev,
+                                        &bdl, ofs, period_bytes,
+                                        !azx_dev->no_period_wakeup);
+               if (ofs < 0)
+                       goto error;
+       }
+       return 0;
+
+ error:
+       dev_dbg(bus->dev, "Too many BDL entries: buffer=%d, period=%d\n",
+               azx_dev->bufsize, period_bytes);
+       return -EINVAL;
+}
+
+/**
+ * snd_hdac_stream_setup_periods - set up BDL entries
+ * @azx_dev: HD-audio core stream to set up
+ *
+ * Set up the buffer descriptor table of the given stream based on the
+ * period and buffer sizes of the assigned PCM substream.
+ */
+int snd_hdac_stream_setup_periods(struct hdac_stream *azx_dev)
+{
+       struct snd_pcm_substream *substream = azx_dev->substream;
+       struct snd_compr_stream *cstream = azx_dev->cstream;
+       struct snd_pcm_runtime *runtime = NULL;
+       struct snd_dma_buffer *dmab;
+
+       if (substream) {
+               runtime = substream->runtime;
+               dmab = snd_pcm_get_dma_buf(substream);
+       } else if (cstream) {
+               dmab = snd_pcm_get_dma_buf(cstream);
+       } else {
+               WARN(1, "No substream or cstream assigned\n");
+               return -EINVAL;
+       }
+
+       return snd_hdac_stream_setup_bdle(azx_dev, dmab, runtime);
+}
+EXPORT_SYMBOL_GPL(snd_hdac_stream_setup_periods);
+
+/**
+ * snd_hdac_stream_set_params - set stream parameters
+ * @azx_dev: HD-audio core stream for which parameters are to be set
+ * @format_val: format value parameter
+ *
+ * Setup the HD-audio core stream parameters from substream of the stream
+ * and passed format value
+ */
+int snd_hdac_stream_set_params(struct hdac_stream *azx_dev,
+                                unsigned int format_val)
+{
+       struct snd_pcm_substream *substream = azx_dev->substream;
+       struct snd_compr_stream *cstream = azx_dev->cstream;
+       unsigned int bufsize, period_bytes;
+       unsigned int no_period_wakeup;
+       int err;
+
+       if (substream) {
+               bufsize = snd_pcm_lib_buffer_bytes(substream);
+               period_bytes = snd_pcm_lib_period_bytes(substream);
+               no_period_wakeup = substream->runtime->no_period_wakeup;
+       } else if (cstream) {
+               bufsize = cstream->runtime->buffer_size;
+               period_bytes = cstream->runtime->fragment_size;
+               no_period_wakeup = 0;
+       } else {
+               return -EINVAL;
+       }
+
+       if (bufsize != azx_dev->bufsize ||
+           period_bytes != azx_dev->period_bytes ||
+           format_val != azx_dev->format_val ||
+           no_period_wakeup != azx_dev->no_period_wakeup) {
+               azx_dev->bufsize = bufsize;
+               azx_dev->period_bytes = period_bytes;
+               azx_dev->format_val = format_val;
+               azx_dev->no_period_wakeup = no_period_wakeup;
+               err = snd_hdac_stream_setup_periods(azx_dev);
+               if (err < 0)
+                       return err;
+       }
+       return 0;
+}
+EXPORT_SYMBOL_GPL(snd_hdac_stream_set_params);
+
+static u64 azx_cc_read(const struct cyclecounter *cc)
+{
+       struct hdac_stream *azx_dev = container_of(cc, struct hdac_stream, cc);
+
+       return snd_hdac_chip_readl(azx_dev->bus, WALLCLK);
+}
+
+static void azx_timecounter_init(struct hdac_stream *azx_dev,
+                                bool force, u64 last)
+{
+       struct timecounter *tc = &azx_dev->tc;
+       struct cyclecounter *cc = &azx_dev->cc;
+       u64 nsec;
+
+       cc->read = azx_cc_read;
+       cc->mask = CLOCKSOURCE_MASK(32);
+
+       /*
+        * Calculate the optimal mult/shift values. The counter wraps
+        * around after ~178.9 seconds.
+        */
+       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);
+       if (force) {
+               /*
+                * force timecounter to use predefined value,
+                * used for synchronized starts
+                */
+               tc->cycle_last = last;
+       }
+}
+
+/**
+ * snd_hdac_stream_timecounter_init - initialize time counter
+ * @azx_dev: HD-audio core stream (master stream)
+ * @streams: bit flags of streams to set up
+ * @start: true for PCM trigger start, false for other cases
+ *
+ * Initializes the time counter of streams marked by the bit flags (each
+ * bit corresponds to the stream index).
+ * The trigger timestamp of PCM substream assigned to the given stream is
+ * updated accordingly, too.
+ */
+void snd_hdac_stream_timecounter_init(struct hdac_stream *azx_dev,
+                                     unsigned int streams, bool start)
+{
+       struct hdac_bus *bus = azx_dev->bus;
+       struct snd_pcm_runtime *runtime = azx_dev->substream->runtime;
+       struct hdac_stream *s;
+       bool inited = false;
+       u64 cycle_last = 0;
+
+       if (!start)
+               goto skip;
+
+       list_for_each_entry(s, &bus->stream_list, list) {
+               if ((streams & (1 << s->index))) {
+                       azx_timecounter_init(s, inited, cycle_last);
+                       if (!inited) {
+                               inited = true;
+                               cycle_last = s->tc.cycle_last;
+                       }
+               }
+       }
+
+skip:
+       snd_pcm_gettime(runtime, &runtime->trigger_tstamp);
+       runtime->trigger_tstamp_latched = true;
+}
+EXPORT_SYMBOL_GPL(snd_hdac_stream_timecounter_init);
+
+/**
+ * snd_hdac_stream_sync_trigger - turn on/off stream sync register
+ * @azx_dev: HD-audio core stream (master stream)
+ * @set: true = set, false = clear
+ * @streams: bit flags of streams to sync
+ * @reg: the stream sync register address
+ */
+void snd_hdac_stream_sync_trigger(struct hdac_stream *azx_dev, bool set,
+                                 unsigned int streams, unsigned int reg)
+{
+       struct hdac_bus *bus = azx_dev->bus;
+       unsigned int val;
+
+       if (!reg)
+               reg = AZX_REG_SSYNC;
+       val = _snd_hdac_chip_readl(bus, reg);
+       if (set)
+               val |= streams;
+       else
+               val &= ~streams;
+       _snd_hdac_chip_writel(bus, reg, val);
+}
+EXPORT_SYMBOL_GPL(snd_hdac_stream_sync_trigger);
+
+/**
+ * snd_hdac_stream_sync - sync with start/stop trigger operation
+ * @azx_dev: HD-audio core stream (master stream)
+ * @start: true = start, false = stop
+ * @streams: bit flags of streams to sync
+ *
+ * For @start = true, wait until all FIFOs get ready.
+ * For @start = false, wait until all RUN bits are cleared.
+ */
+void snd_hdac_stream_sync(struct hdac_stream *azx_dev, bool start,
+                         unsigned int streams)
+{
+       struct hdac_bus *bus = azx_dev->bus;
+       int nwait, timeout;
+       struct hdac_stream *s;
+
+       for (timeout = 5000; timeout; timeout--) {
+               nwait = 0;
+               list_for_each_entry(s, &bus->stream_list, list) {
+                       if (!(streams & (1 << s->index)))
+                               continue;
+
+                       if (start) {
+                               /* check FIFO gets ready */
+                               if (!(snd_hdac_stream_readb(s, SD_STS) &
+                                     SD_STS_FIFO_READY))
+                                       nwait++;
+                       } else {
+                               /* check RUN bit is cleared */
+                               if (snd_hdac_stream_readb(s, SD_CTL) &
+                                   SD_CTL_DMA_START) {
+                                       nwait++;
+                                       /*
+                                        * Perform stream reset if DMA RUN
+                                        * bit not cleared within given timeout
+                                        */
+                                       if (timeout == 1)
+                                               snd_hdac_stream_reset(s);
+                               }
+                       }
+               }
+               if (!nwait)
+                       break;
+               cpu_relax();
+       }
+}
+EXPORT_SYMBOL_GPL(snd_hdac_stream_sync);
+
+/**
+ * snd_hdac_stream_spbcap_enable - enable SPIB for a stream
+ * @bus: HD-audio core bus
+ * @enable: flag to enable/disable SPIB
+ * @index: stream index for which SPIB need to be enabled
+ */
+void snd_hdac_stream_spbcap_enable(struct hdac_bus *bus,
+                                  bool enable, int index)
+{
+       u32 mask = 0;
+
+       if (!bus->spbcap) {
+               dev_err(bus->dev, "Address of SPB capability is NULL\n");
+               return;
+       }
+
+       mask |= (1 << index);
+
+       if (enable)
+               snd_hdac_updatel(bus->spbcap, AZX_REG_SPB_SPBFCCTL, mask, mask);
+       else
+               snd_hdac_updatel(bus->spbcap, AZX_REG_SPB_SPBFCCTL, mask, 0);
+}
+EXPORT_SYMBOL_GPL(snd_hdac_stream_spbcap_enable);
+
+/**
+ * snd_hdac_stream_set_spib - sets the spib value of a stream
+ * @bus: HD-audio core bus
+ * @azx_dev: hdac_stream
+ * @value: spib value to set
+ */
+int snd_hdac_stream_set_spib(struct hdac_bus *bus,
+                            struct hdac_stream *azx_dev, u32 value)
+{
+       if (!bus->spbcap) {
+               dev_err(bus->dev, "Address of SPB capability is NULL\n");
+               return -EINVAL;
+       }
+
+       writel(value, azx_dev->spib_addr);
+
+       return 0;
+}
+EXPORT_SYMBOL_GPL(snd_hdac_stream_set_spib);
+
+/**
+ * snd_hdac_stream_drsm_enable - enable DMA resume for a stream
+ * @bus: HD-audio core bus
+ * @enable: flag to enable/disable DRSM
+ * @index: stream index for which DRSM need to be enabled
+ */
+void snd_hdac_stream_drsm_enable(struct hdac_bus *bus,
+                                bool enable, int index)
+{
+       u32 mask = 0;
+
+       if (!bus->drsmcap) {
+               dev_err(bus->dev, "Address of DRSM capability is NULL\n");
+               return;
+       }
+
+       mask |= (1 << index);
+
+       if (enable)
+               snd_hdac_updatel(bus->drsmcap, AZX_REG_DRSM_CTL, mask, mask);
+       else
+               snd_hdac_updatel(bus->drsmcap, AZX_REG_DRSM_CTL, mask, 0);
+}
+EXPORT_SYMBOL_GPL(snd_hdac_stream_drsm_enable);
+
+/*
+ * snd_hdac_stream_wait_drsm - wait for HW to clear RSM for a stream
+ * @azx_dev: HD-audio core stream to await RSM for
+ *
+ * Returns 0 on success and -ETIMEDOUT upon a timeout.
+ */
+int snd_hdac_stream_wait_drsm(struct hdac_stream *azx_dev)
+{
+       struct hdac_bus *bus = azx_dev->bus;
+       u32 mask, reg;
+       int ret;
+
+       mask = 1 << azx_dev->index;
+
+       ret = read_poll_timeout(snd_hdac_reg_readl, reg, !(reg & mask), 250, 2000, false, bus,
+                               bus->drsmcap + AZX_REG_DRSM_CTL);
+       if (ret)
+               dev_dbg(bus->dev, "polling RSM 0x%08x failed: %d\n", mask, ret);
+       return ret;
+}
+EXPORT_SYMBOL_GPL(snd_hdac_stream_wait_drsm);
+
+/**
+ * snd_hdac_stream_set_dpibr - sets the dpibr value of a stream
+ * @bus: HD-audio core bus
+ * @azx_dev: hdac_stream
+ * @value: dpib value to set
+ */
+int snd_hdac_stream_set_dpibr(struct hdac_bus *bus,
+                             struct hdac_stream *azx_dev, u32 value)
+{
+       if (!bus->drsmcap) {
+               dev_err(bus->dev, "Address of DRSM capability is NULL\n");
+               return -EINVAL;
+       }
+
+       writel(value, azx_dev->dpibr_addr);
+
+       return 0;
+}
+EXPORT_SYMBOL_GPL(snd_hdac_stream_set_dpibr);
+
+/**
+ * snd_hdac_stream_set_lpib - sets the lpib value of a stream
+ * @azx_dev: hdac_stream
+ * @value: lpib value to set
+ */
+int snd_hdac_stream_set_lpib(struct hdac_stream *azx_dev, u32 value)
+{
+       snd_hdac_stream_writel(azx_dev, SD_LPIB, value);
+
+       return 0;
+}
+EXPORT_SYMBOL_GPL(snd_hdac_stream_set_lpib);
+
+#ifdef CONFIG_SND_HDA_DSP_LOADER
+/**
+ * snd_hdac_dsp_prepare - prepare for DSP loading
+ * @azx_dev: HD-audio core stream used for DSP loading
+ * @format: HD-audio stream format
+ * @byte_size: data chunk byte size
+ * @bufp: allocated buffer
+ *
+ * Allocate the buffer for the given size and set up the given stream for
+ * DSP loading.  Returns the stream tag (>= 0), or a negative error code.
+ */
+int snd_hdac_dsp_prepare(struct hdac_stream *azx_dev, unsigned int format,
+                        unsigned int byte_size, struct snd_dma_buffer *bufp)
+{
+       struct hdac_bus *bus = azx_dev->bus;
+       int err;
+
+       snd_hdac_dsp_lock(azx_dev);
+       spin_lock_irq(&bus->reg_lock);
+       if (azx_dev->running || azx_dev->locked) {
+               spin_unlock_irq(&bus->reg_lock);
+               err = -EBUSY;
+               goto unlock;
+       }
+       azx_dev->locked = true;
+       spin_unlock_irq(&bus->reg_lock);
+
+       err = snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV_SG, bus->dev,
+                                 byte_size, bufp);
+       if (err < 0)
+               goto err_alloc;
+
+       azx_dev->substream = NULL;
+       azx_dev->bufsize = byte_size;
+       /* It is recommended to transfer the firmware in two or more chunks. */
+       azx_dev->period_bytes = byte_size / 2;
+       azx_dev->format_val = format;
+       azx_dev->no_period_wakeup = 1;
+
+       snd_hdac_stream_reset(azx_dev);
+
+       err = snd_hdac_stream_setup_bdle(azx_dev, bufp, NULL);
+       if (err < 0)
+               goto error;
+
+       snd_hdac_stream_setup(azx_dev, true);
+       snd_hdac_dsp_unlock(azx_dev);
+       return azx_dev->stream_tag;
+
+ error:
+       snd_dma_free_pages(bufp);
+ err_alloc:
+       spin_lock_irq(&bus->reg_lock);
+       azx_dev->locked = false;
+       spin_unlock_irq(&bus->reg_lock);
+ unlock:
+       snd_hdac_dsp_unlock(azx_dev);
+       return err;
+}
+EXPORT_SYMBOL_GPL(snd_hdac_dsp_prepare);
+
+/**
+ * snd_hdac_dsp_trigger - start / stop DSP loading
+ * @azx_dev: HD-audio core stream used for DSP loading
+ * @start: trigger start or stop
+ */
+void snd_hdac_dsp_trigger(struct hdac_stream *azx_dev, bool start)
+{
+       if (start)
+               snd_hdac_stream_start(azx_dev);
+       else
+               snd_hdac_stream_stop(azx_dev);
+}
+EXPORT_SYMBOL_GPL(snd_hdac_dsp_trigger);
+
+/**
+ * snd_hdac_dsp_cleanup - clean up the stream from DSP loading to normal
+ * @azx_dev: HD-audio core stream used for DSP loading
+ * @dmab: buffer used by DSP loading
+ */
+void snd_hdac_dsp_cleanup(struct hdac_stream *azx_dev,
+                         struct snd_dma_buffer *dmab)
+{
+       struct hdac_bus *bus = azx_dev->bus;
+
+       if (!dmab->area || !azx_dev->locked)
+               return;
+
+       snd_hdac_dsp_lock(azx_dev);
+       /* reset BDL address */
+       snd_hdac_stream_writel(azx_dev, SD_BDLPL, 0);
+       snd_hdac_stream_writel(azx_dev, SD_BDLPU, 0);
+       snd_hdac_stream_writel(azx_dev, SD_CTL, 0);
+       azx_dev->bufsize = 0;
+       azx_dev->period_bytes = 0;
+       azx_dev->format_val = 0;
+
+       snd_dma_free_pages(dmab);
+       dmab->area = NULL;
+
+       spin_lock_irq(&bus->reg_lock);
+       azx_dev->locked = false;
+       spin_unlock_irq(&bus->reg_lock);
+       snd_hdac_dsp_unlock(azx_dev);
+}
+EXPORT_SYMBOL_GPL(snd_hdac_dsp_cleanup);
+#endif /* CONFIG_SND_HDA_DSP_LOADER */
diff --git a/sound/hda/core/sysfs.c b/sound/hda/core/sysfs.c
new file mode 100644 (file)
index 0000000..bffe528
--- /dev/null
@@ -0,0 +1,469 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * sysfs support for HD-audio core device
+ */
+
+#include <linux/slab.h>
+#include <linux/sysfs.h>
+#include <linux/device.h>
+#include <sound/core.h>
+#include <sound/hdaudio.h>
+#include "local.h"
+
+struct hdac_widget_tree {
+       struct kobject *root;
+       struct kobject *afg;
+       struct kobject **nodes;
+};
+
+#define CODEC_ATTR(type)                                       \
+static ssize_t type##_show(struct device *dev,                 \
+                          struct device_attribute *attr,       \
+                          char *buf)                           \
+{                                                              \
+       struct hdac_device *codec = dev_to_hdac_dev(dev);       \
+       return sysfs_emit(buf, "0x%x\n", codec->type);          \
+} \
+static DEVICE_ATTR_RO(type)
+
+#define CODEC_ATTR_STR(type)                                   \
+static ssize_t type##_show(struct device *dev,                 \
+                            struct device_attribute *attr,     \
+                                       char *buf)              \
+{                                                              \
+       struct hdac_device *codec = dev_to_hdac_dev(dev);       \
+       return sysfs_emit(buf, "%s\n",                          \
+                         codec->type ? codec->type : "");      \
+} \
+static DEVICE_ATTR_RO(type)
+
+CODEC_ATTR(type);
+CODEC_ATTR(vendor_id);
+CODEC_ATTR(subsystem_id);
+CODEC_ATTR(revision_id);
+CODEC_ATTR(afg);
+CODEC_ATTR(mfg);
+CODEC_ATTR_STR(vendor_name);
+CODEC_ATTR_STR(chip_name);
+
+static ssize_t modalias_show(struct device *dev, struct device_attribute *attr,
+                            char *buf)
+{
+       return snd_hdac_codec_modalias(dev_to_hdac_dev(dev), buf, 256);
+}
+static DEVICE_ATTR_RO(modalias);
+
+static struct attribute *hdac_dev_attrs[] = {
+       &dev_attr_type.attr,
+       &dev_attr_vendor_id.attr,
+       &dev_attr_subsystem_id.attr,
+       &dev_attr_revision_id.attr,
+       &dev_attr_afg.attr,
+       &dev_attr_mfg.attr,
+       &dev_attr_vendor_name.attr,
+       &dev_attr_chip_name.attr,
+       &dev_attr_modalias.attr,
+       NULL
+};
+
+static const struct attribute_group hdac_dev_attr_group = {
+       .attrs  = hdac_dev_attrs,
+};
+
+const struct attribute_group *hdac_dev_attr_groups[] = {
+       &hdac_dev_attr_group,
+       NULL
+};
+
+/*
+ * Widget tree sysfs
+ *
+ * This is a tree showing the attributes of each widget.  It appears like
+ * /sys/bus/hdaudioC0D0/widgets/04/caps
+ */
+
+struct widget_attribute;
+
+struct widget_attribute {
+       struct attribute        attr;
+       ssize_t (*show)(struct hdac_device *codec, hda_nid_t nid,
+                       struct widget_attribute *attr, char *buf);
+       ssize_t (*store)(struct hdac_device *codec, hda_nid_t nid,
+                        struct widget_attribute *attr,
+                        const char *buf, size_t count);
+};
+
+static int get_codec_nid(struct kobject *kobj, struct hdac_device **codecp)
+{
+       struct device *dev = kobj_to_dev(kobj->parent->parent);
+       int nid;
+       ssize_t ret;
+
+       ret = kstrtoint(kobj->name, 16, &nid);
+       if (ret < 0)
+               return ret;
+       *codecp = dev_to_hdac_dev(dev);
+       return nid;
+}
+
+static ssize_t widget_attr_show(struct kobject *kobj, struct attribute *attr,
+                               char *buf)
+{
+       struct widget_attribute *wid_attr =
+               container_of(attr, struct widget_attribute, attr);
+       struct hdac_device *codec;
+       int nid;
+
+       if (!wid_attr->show)
+               return -EIO;
+       nid = get_codec_nid(kobj, &codec);
+       if (nid < 0)
+               return nid;
+       return wid_attr->show(codec, nid, wid_attr, buf);
+}
+
+static ssize_t widget_attr_store(struct kobject *kobj, struct attribute *attr,
+                                const char *buf, size_t count)
+{
+       struct widget_attribute *wid_attr =
+               container_of(attr, struct widget_attribute, attr);
+       struct hdac_device *codec;
+       int nid;
+
+       if (!wid_attr->store)
+               return -EIO;
+       nid = get_codec_nid(kobj, &codec);
+       if (nid < 0)
+               return nid;
+       return wid_attr->store(codec, nid, wid_attr, buf, count);
+}
+
+static const struct sysfs_ops widget_sysfs_ops = {
+       .show   = widget_attr_show,
+       .store  = widget_attr_store,
+};
+
+static void widget_release(struct kobject *kobj)
+{
+       kfree(kobj);
+}
+
+static const struct kobj_type widget_ktype = {
+       .release        = widget_release,
+       .sysfs_ops      = &widget_sysfs_ops,
+};
+
+#define WIDGET_ATTR_RO(_name) \
+       struct widget_attribute wid_attr_##_name = __ATTR_RO(_name)
+#define WIDGET_ATTR_RW(_name) \
+       struct widget_attribute wid_attr_##_name = __ATTR_RW(_name)
+
+static ssize_t caps_show(struct hdac_device *codec, hda_nid_t nid,
+                       struct widget_attribute *attr, char *buf)
+{
+       return sysfs_emit(buf, "0x%08x\n", snd_hdac_get_wcaps(codec, nid));
+}
+
+static ssize_t pin_caps_show(struct hdac_device *codec, hda_nid_t nid,
+                            struct widget_attribute *attr, char *buf)
+{
+       if (snd_hdac_get_wcaps_type(snd_hdac_get_wcaps(codec, nid)) != AC_WID_PIN)
+               return 0;
+       return sysfs_emit(buf, "0x%08x\n",
+                         snd_hdac_read_parm(codec, nid, AC_PAR_PIN_CAP));
+}
+
+static ssize_t pin_cfg_show(struct hdac_device *codec, hda_nid_t nid,
+                           struct widget_attribute *attr, char *buf)
+{
+       unsigned int val;
+
+       if (snd_hdac_get_wcaps_type(snd_hdac_get_wcaps(codec, nid)) != AC_WID_PIN)
+               return 0;
+       if (snd_hdac_read(codec, nid, AC_VERB_GET_CONFIG_DEFAULT, 0, &val))
+               return 0;
+       return sysfs_emit(buf, "0x%08x\n", val);
+}
+
+static bool has_pcm_cap(struct hdac_device *codec, hda_nid_t nid)
+{
+       if (nid == codec->afg || nid == codec->mfg)
+               return true;
+       switch (snd_hdac_get_wcaps_type(snd_hdac_get_wcaps(codec, nid))) {
+       case AC_WID_AUD_OUT:
+       case AC_WID_AUD_IN:
+               return true;
+       default:
+               return false;
+       }
+}
+
+static ssize_t pcm_caps_show(struct hdac_device *codec, hda_nid_t nid,
+                            struct widget_attribute *attr, char *buf)
+{
+       if (!has_pcm_cap(codec, nid))
+               return 0;
+       return sysfs_emit(buf, "0x%08x\n",
+                         snd_hdac_read_parm(codec, nid, AC_PAR_PCM));
+}
+
+static ssize_t pcm_formats_show(struct hdac_device *codec, hda_nid_t nid,
+                               struct widget_attribute *attr, char *buf)
+{
+       if (!has_pcm_cap(codec, nid))
+               return 0;
+       return sysfs_emit(buf, "0x%08x\n",
+                         snd_hdac_read_parm(codec, nid, AC_PAR_STREAM));
+}
+
+static ssize_t amp_in_caps_show(struct hdac_device *codec, hda_nid_t nid,
+                               struct widget_attribute *attr, char *buf)
+{
+       if (nid != codec->afg && !(snd_hdac_get_wcaps(codec, nid) & AC_WCAP_IN_AMP))
+               return 0;
+       return sysfs_emit(buf, "0x%08x\n",
+                         snd_hdac_read_parm(codec, nid, AC_PAR_AMP_IN_CAP));
+}
+
+static ssize_t amp_out_caps_show(struct hdac_device *codec, hda_nid_t nid,
+                                struct widget_attribute *attr, char *buf)
+{
+       if (nid != codec->afg && !(snd_hdac_get_wcaps(codec, nid) & AC_WCAP_OUT_AMP))
+               return 0;
+       return sysfs_emit(buf, "0x%08x\n",
+                         snd_hdac_read_parm(codec, nid, AC_PAR_AMP_OUT_CAP));
+}
+
+static ssize_t power_caps_show(struct hdac_device *codec, hda_nid_t nid,
+                              struct widget_attribute *attr, char *buf)
+{
+       if (nid != codec->afg && !(snd_hdac_get_wcaps(codec, nid) & AC_WCAP_POWER))
+               return 0;
+       return sysfs_emit(buf, "0x%08x\n",
+                         snd_hdac_read_parm(codec, nid, AC_PAR_POWER_STATE));
+}
+
+static ssize_t gpio_caps_show(struct hdac_device *codec, hda_nid_t nid,
+                             struct widget_attribute *attr, char *buf)
+{
+       return sysfs_emit(buf, "0x%08x\n",
+                         snd_hdac_read_parm(codec, nid, AC_PAR_GPIO_CAP));
+}
+
+static ssize_t connections_show(struct hdac_device *codec, hda_nid_t nid,
+                               struct widget_attribute *attr, char *buf)
+{
+       hda_nid_t list[32];
+       int i, nconns;
+       ssize_t ret = 0;
+
+       nconns = snd_hdac_get_connections(codec, nid, list, ARRAY_SIZE(list));
+       if (nconns <= 0)
+               return nconns;
+       for (i = 0; i < nconns; i++)
+               ret += sysfs_emit_at(buf,  ret, "%s0x%02x", i ? " " : "", list[i]);
+       ret += sysfs_emit_at(buf, ret, "\n");
+       return ret;
+}
+
+static WIDGET_ATTR_RO(caps);
+static WIDGET_ATTR_RO(pin_caps);
+static WIDGET_ATTR_RO(pin_cfg);
+static WIDGET_ATTR_RO(pcm_caps);
+static WIDGET_ATTR_RO(pcm_formats);
+static WIDGET_ATTR_RO(amp_in_caps);
+static WIDGET_ATTR_RO(amp_out_caps);
+static WIDGET_ATTR_RO(power_caps);
+static WIDGET_ATTR_RO(gpio_caps);
+static WIDGET_ATTR_RO(connections);
+
+static struct attribute *widget_node_attrs[] = {
+       &wid_attr_caps.attr,
+       &wid_attr_pin_caps.attr,
+       &wid_attr_pin_cfg.attr,
+       &wid_attr_pcm_caps.attr,
+       &wid_attr_pcm_formats.attr,
+       &wid_attr_amp_in_caps.attr,
+       &wid_attr_amp_out_caps.attr,
+       &wid_attr_power_caps.attr,
+       &wid_attr_connections.attr,
+       NULL,
+};
+
+static struct attribute *widget_afg_attrs[] = {
+       &wid_attr_pcm_caps.attr,
+       &wid_attr_pcm_formats.attr,
+       &wid_attr_amp_in_caps.attr,
+       &wid_attr_amp_out_caps.attr,
+       &wid_attr_power_caps.attr,
+       &wid_attr_gpio_caps.attr,
+       NULL,
+};
+
+static const struct attribute_group widget_node_group = {
+       .attrs = widget_node_attrs,
+};
+
+static const struct attribute_group widget_afg_group = {
+       .attrs = widget_afg_attrs,
+};
+
+static void free_widget_node(struct kobject *kobj,
+                            const struct attribute_group *group)
+{
+       if (kobj) {
+               sysfs_remove_group(kobj, group);
+               kobject_put(kobj);
+       }
+}
+
+static void widget_tree_free(struct hdac_device *codec)
+{
+       struct hdac_widget_tree *tree = codec->widgets;
+       struct kobject **p;
+
+       if (!tree)
+               return;
+       free_widget_node(tree->afg, &widget_afg_group);
+       if (tree->nodes) {
+               for (p = tree->nodes; *p; p++)
+                       free_widget_node(*p, &widget_node_group);
+               kfree(tree->nodes);
+       }
+       kobject_put(tree->root);
+       kfree(tree);
+       codec->widgets = NULL;
+}
+
+static int add_widget_node(struct kobject *parent, hda_nid_t nid,
+                          const struct attribute_group *group,
+                          struct kobject **res)
+{
+       struct kobject *kobj = kzalloc(sizeof(*kobj), GFP_KERNEL);
+       int err;
+
+       if (!kobj)
+               return -ENOMEM;
+       kobject_init(kobj, &widget_ktype);
+       err = kobject_add(kobj, parent, "%02x", nid);
+       if (err < 0) {
+               kobject_put(kobj);
+               return err;
+       }
+       err = sysfs_create_group(kobj, group);
+       if (err < 0) {
+               kobject_put(kobj);
+               return err;
+       }
+
+       *res = kobj;
+       return 0;
+}
+
+static int widget_tree_create(struct hdac_device *codec)
+{
+       struct hdac_widget_tree *tree;
+       int i, err;
+       hda_nid_t nid;
+
+       tree = codec->widgets = kzalloc(sizeof(*tree), GFP_KERNEL);
+       if (!tree)
+               return -ENOMEM;
+
+       tree->root = kobject_create_and_add("widgets", &codec->dev.kobj);
+       if (!tree->root)
+               return -ENOMEM;
+
+       tree->nodes = kcalloc(codec->num_nodes + 1, sizeof(*tree->nodes),
+                             GFP_KERNEL);
+       if (!tree->nodes)
+               return -ENOMEM;
+
+       for (i = 0, nid = codec->start_nid; i < codec->num_nodes; i++, nid++) {
+               err = add_widget_node(tree->root, nid, &widget_node_group,
+                                     &tree->nodes[i]);
+               if (err < 0)
+                       return err;
+       }
+
+       if (codec->afg) {
+               err = add_widget_node(tree->root, codec->afg,
+                                     &widget_afg_group, &tree->afg);
+               if (err < 0)
+                       return err;
+       }
+
+       kobject_uevent(tree->root, KOBJ_CHANGE);
+       return 0;
+}
+
+/* call with codec->widget_lock held */
+int hda_widget_sysfs_init(struct hdac_device *codec)
+{
+       int err;
+
+       if (codec->widgets)
+               return 0; /* already created */
+
+       err = widget_tree_create(codec);
+       if (err < 0) {
+               widget_tree_free(codec);
+               return err;
+       }
+
+       return 0;
+}
+
+/* call with codec->widget_lock held */
+void hda_widget_sysfs_exit(struct hdac_device *codec)
+{
+       widget_tree_free(codec);
+}
+
+/* call with codec->widget_lock held */
+int hda_widget_sysfs_reinit(struct hdac_device *codec,
+                           hda_nid_t start_nid, int num_nodes)
+{
+       struct hdac_widget_tree *tree;
+       hda_nid_t end_nid = start_nid + num_nodes;
+       hda_nid_t nid;
+       int i;
+
+       if (!codec->widgets)
+               return 0;
+
+       tree = kmemdup(codec->widgets, sizeof(*tree), GFP_KERNEL);
+       if (!tree)
+               return -ENOMEM;
+
+       tree->nodes = kcalloc(num_nodes + 1, sizeof(*tree->nodes), GFP_KERNEL);
+       if (!tree->nodes) {
+               kfree(tree);
+               return -ENOMEM;
+       }
+
+       /* prune non-existing nodes */
+       for (i = 0, nid = codec->start_nid; i < codec->num_nodes; i++, nid++) {
+               if (nid < start_nid || nid >= end_nid)
+                       free_widget_node(codec->widgets->nodes[i],
+                                        &widget_node_group);
+       }
+
+       /* add new nodes */
+       for (i = 0, nid = start_nid; i < num_nodes; i++, nid++) {
+               if (nid < codec->start_nid || nid >= codec->end_nid)
+                       add_widget_node(tree->root, nid, &widget_node_group,
+                                       &tree->nodes[i]);
+               else
+                       tree->nodes[i] =
+                               codec->widgets->nodes[nid - codec->start_nid];
+       }
+
+       /* replace with the new tree */
+       kfree(codec->widgets->nodes);
+       kfree(codec->widgets);
+       codec->widgets = tree;
+
+       kobject_uevent(tree->root, KOBJ_CHANGE);
+       return 0;
+}
diff --git a/sound/hda/core/trace.c b/sound/hda/core/trace.c
new file mode 100644 (file)
index 0000000..ca2d6bd
--- /dev/null
@@ -0,0 +1,6 @@
+/*
+ * tracepoint definitions for HD-audio core drivers
+ */
+
+#define CREATE_TRACE_POINTS
+#include "trace.h"
diff --git a/sound/hda/core/trace.h b/sound/hda/core/trace.h
new file mode 100644 (file)
index 0000000..280c42f
--- /dev/null
@@ -0,0 +1,101 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+#undef TRACE_SYSTEM
+#define TRACE_SYSTEM hda
+
+#if !defined(__HDAC_TRACE_H) || defined(TRACE_HEADER_MULTI_READ)
+#define __HDAC_TRACE_H
+
+#include <linux/tracepoint.h>
+#include <linux/device.h>
+#include <sound/hdaudio.h>
+
+#ifndef HDAC_MSG_MAX
+#define HDAC_MSG_MAX   500
+#endif
+
+struct hdac_bus;
+struct hdac_codec;
+
+TRACE_EVENT(hda_send_cmd,
+       TP_PROTO(struct hdac_bus *bus, unsigned int cmd),
+       TP_ARGS(bus, cmd),
+       TP_STRUCT__entry(
+               __string(name, dev_name((bus)->dev))
+               __field(u32, cmd)
+       ),
+       TP_fast_assign(
+               __assign_str(name);
+               __entry->cmd = cmd;
+       ),
+       TP_printk("[%s:%d] val=0x%08x", __get_str(name), __entry->cmd >> 28, __entry->cmd)
+);
+
+TRACE_EVENT(hda_get_response,
+       TP_PROTO(struct hdac_bus *bus, unsigned int addr, unsigned int res),
+       TP_ARGS(bus, addr, res),
+       TP_STRUCT__entry(
+               __string(name, dev_name((bus)->dev))
+               __field(u32, addr)
+               __field(u32, res)
+       ),
+       TP_fast_assign(
+               __assign_str(name);
+               __entry->addr = addr;
+               __entry->res = res;
+       ),
+       TP_printk("[%s:%d] val=0x%08x", __get_str(name), __entry->addr, __entry->res)
+);
+
+TRACE_EVENT(hda_unsol_event,
+       TP_PROTO(struct hdac_bus *bus, u32 res, u32 res_ex),
+       TP_ARGS(bus, res, res_ex),
+       TP_STRUCT__entry(
+               __string(name, dev_name((bus)->dev))
+               __field(u32, res)
+               __field(u32, res_ex)
+       ),
+       TP_fast_assign(
+               __assign_str(name);
+               __entry->res = res;
+               __entry->res_ex = res_ex;
+       ),
+       TP_printk("[%s:%d] res=0x%08x, res_ex=0x%08x", __get_str(name),
+                 __entry->res_ex & 0x0f, __entry->res, __entry->res_ex)
+);
+
+DECLARE_EVENT_CLASS(hdac_stream,
+       TP_PROTO(struct hdac_bus *bus, struct hdac_stream *azx_dev),
+
+       TP_ARGS(bus, azx_dev),
+
+       TP_STRUCT__entry(
+               __field(unsigned char, stream_tag)
+       ),
+
+       TP_fast_assign(
+               __entry->stream_tag = (azx_dev)->stream_tag;
+       ),
+
+       TP_printk("stream_tag: %d", __entry->stream_tag)
+);
+
+DEFINE_EVENT(hdac_stream, snd_hdac_stream_start,
+       TP_PROTO(struct hdac_bus *bus, struct hdac_stream *azx_dev),
+       TP_ARGS(bus, azx_dev)
+);
+
+DEFINE_EVENT(hdac_stream, snd_hdac_stream_stop,
+       TP_PROTO(struct hdac_bus *bus, struct hdac_stream *azx_dev),
+       TP_ARGS(bus, azx_dev)
+);
+
+#endif /* __HDAC_TRACE_H */
+
+/* This part must be outside protection */
+#undef TRACE_INCLUDE_PATH
+#define TRACE_INCLUDE_PATH .
+
+#undef TRACE_INCLUDE_FILE
+#define TRACE_INCLUDE_FILE trace
+
+#include <trace/define_trace.h>
diff --git a/sound/hda/ext/Makefile b/sound/hda/ext/Makefile
deleted file mode 100644 (file)
index 05883fb..0000000
+++ /dev/null
@@ -1,4 +0,0 @@
-# SPDX-License-Identifier: GPL-2.0-only
-snd-hda-ext-core-y := hdac_ext_bus.o hdac_ext_controller.o hdac_ext_stream.o
-
-obj-$(CONFIG_SND_HDA_EXT_CORE) += snd-hda-ext-core.o
diff --git a/sound/hda/ext/hdac_ext_bus.c b/sound/hda/ext/hdac_ext_bus.c
deleted file mode 100644 (file)
index 6004ea1..0000000
+++ /dev/null
@@ -1,142 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0-only
-/*
- *  hdac-ext-bus.c - HD-audio extended core bus functions.
- *
- *  Copyright (C) 2014-2015 Intel Corp
- *  Author: Jeeja KP <jeeja.kp@intel.com>
- *  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- *
- * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- */
-
-#include <linux/module.h>
-#include <linux/slab.h>
-#include <linux/io.h>
-#include <sound/hdaudio_ext.h>
-
-MODULE_DESCRIPTION("HDA extended core");
-MODULE_LICENSE("GPL v2");
-
-/**
- * snd_hdac_ext_bus_init - initialize a HD-audio extended bus
- * @bus: the pointer to HDAC bus object
- * @dev: device pointer
- * @ops: bus verb operators
- * @ext_ops: operators used for ASoC HDA codec drivers
- *
- * Returns 0 if successful, or a negative error code.
- */
-int snd_hdac_ext_bus_init(struct hdac_bus *bus, struct device *dev,
-                       const struct hdac_bus_ops *ops,
-                       const struct hdac_ext_bus_ops *ext_ops)
-{
-       int ret;
-
-       ret = snd_hdac_bus_init(bus, dev, ops);
-       if (ret < 0)
-               return ret;
-
-       bus->ext_ops = ext_ops;
-       /* FIXME:
-        * Currently only one bus is supported, if there is device with more
-        * buses, bus->idx should be greater than 0, but there needs to be a
-        * reliable way to always assign same number.
-        */
-       bus->idx = 0;
-       bus->cmd_dma_state = true;
-
-       return 0;
-}
-EXPORT_SYMBOL_GPL(snd_hdac_ext_bus_init);
-
-/**
- * snd_hdac_ext_bus_exit - clean up a HD-audio extended bus
- * @bus: the pointer to HDAC bus object
- */
-void snd_hdac_ext_bus_exit(struct hdac_bus *bus)
-{
-       snd_hdac_bus_exit(bus);
-       WARN_ON(!list_empty(&bus->hlink_list));
-}
-EXPORT_SYMBOL_GPL(snd_hdac_ext_bus_exit);
-
-/**
- * snd_hdac_ext_bus_device_remove - remove HD-audio extended codec base devices
- *
- * @bus: the pointer to HDAC bus object
- */
-void snd_hdac_ext_bus_device_remove(struct hdac_bus *bus)
-{
-       struct hdac_device *codec, *__codec;
-       /*
-        * we need to remove all the codec devices objects created in the
-        * snd_hdac_ext_bus_device_init
-        */
-       list_for_each_entry_safe(codec, __codec, &bus->codec_list, list) {
-               snd_hdac_device_unregister(codec);
-               put_device(&codec->dev);
-       }
-}
-EXPORT_SYMBOL_GPL(snd_hdac_ext_bus_device_remove);
-#define dev_to_hdac(dev) (container_of((dev), \
-                       struct hdac_device, dev))
-
-static inline struct hdac_driver *get_hdrv(struct device *dev)
-{
-       struct hdac_driver *hdrv = drv_to_hdac_driver(dev->driver);
-       return hdrv;
-}
-
-static inline struct hdac_device *get_hdev(struct device *dev)
-{
-       struct hdac_device *hdev = dev_to_hdac_dev(dev);
-       return hdev;
-}
-
-static int hda_ext_drv_probe(struct device *dev)
-{
-       return (get_hdrv(dev))->probe(get_hdev(dev));
-}
-
-static int hdac_ext_drv_remove(struct device *dev)
-{
-       return (get_hdrv(dev))->remove(get_hdev(dev));
-}
-
-static void hdac_ext_drv_shutdown(struct device *dev)
-{
-       return (get_hdrv(dev))->shutdown(get_hdev(dev));
-}
-
-/**
- * snd_hda_ext_driver_register - register a driver for ext hda devices
- *
- * @drv: ext hda driver structure
- */
-int snd_hda_ext_driver_register(struct hdac_driver *drv)
-{
-       drv->type = HDA_DEV_ASOC;
-       drv->driver.bus = &snd_hda_bus_type;
-       /* we use default match */
-
-       if (drv->probe)
-               drv->driver.probe = hda_ext_drv_probe;
-       if (drv->remove)
-               drv->driver.remove = hdac_ext_drv_remove;
-       if (drv->shutdown)
-               drv->driver.shutdown = hdac_ext_drv_shutdown;
-
-       return driver_register(&drv->driver);
-}
-EXPORT_SYMBOL_GPL(snd_hda_ext_driver_register);
-
-/**
- * snd_hda_ext_driver_unregister - unregister a driver for ext hda devices
- *
- * @drv: ext hda driver structure
- */
-void snd_hda_ext_driver_unregister(struct hdac_driver *drv)
-{
-       driver_unregister(&drv->driver);
-}
-EXPORT_SYMBOL_GPL(snd_hda_ext_driver_unregister);
diff --git a/sound/hda/ext/hdac_ext_controller.c b/sound/hda/ext/hdac_ext_controller.c
deleted file mode 100644 (file)
index c847544..0000000
+++ /dev/null
@@ -1,412 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0-only
-/*
- *  hdac-ext-controller.c - HD-audio extended controller functions.
- *
- *  Copyright (C) 2014-2015 Intel Corp
- *  Author: Jeeja KP <jeeja.kp@intel.com>
- *  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- *
- * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- */
-
-#include <linux/bitfield.h>
-#include <linux/delay.h>
-#include <linux/slab.h>
-#include <sound/hda_register.h>
-#include <sound/hdaudio_ext.h>
-
-/*
- * processing pipe helpers - these helpers are useful for dealing with HDA
- * new capability of processing pipelines
- */
-
-/**
- * snd_hdac_ext_bus_ppcap_enable - enable/disable processing pipe capability
- * @bus: the pointer to HDAC bus object
- * @enable: flag to turn on/off the capability
- */
-void snd_hdac_ext_bus_ppcap_enable(struct hdac_bus *bus, bool enable)
-{
-
-       if (!bus->ppcap) {
-               dev_err(bus->dev, "Address of PP capability is NULL");
-               return;
-       }
-
-       if (enable)
-               snd_hdac_updatel(bus->ppcap, AZX_REG_PP_PPCTL,
-                                AZX_PPCTL_GPROCEN, AZX_PPCTL_GPROCEN);
-       else
-               snd_hdac_updatel(bus->ppcap, AZX_REG_PP_PPCTL,
-                                AZX_PPCTL_GPROCEN, 0);
-}
-EXPORT_SYMBOL_GPL(snd_hdac_ext_bus_ppcap_enable);
-
-/**
- * snd_hdac_ext_bus_ppcap_int_enable - ppcap interrupt enable/disable
- * @bus: the pointer to HDAC bus object
- * @enable: flag to enable/disable interrupt
- */
-void snd_hdac_ext_bus_ppcap_int_enable(struct hdac_bus *bus, bool enable)
-{
-
-       if (!bus->ppcap) {
-               dev_err(bus->dev, "Address of PP capability is NULL\n");
-               return;
-       }
-
-       if (enable)
-               snd_hdac_updatel(bus->ppcap, AZX_REG_PP_PPCTL,
-                                AZX_PPCTL_PIE, AZX_PPCTL_PIE);
-       else
-               snd_hdac_updatel(bus->ppcap, AZX_REG_PP_PPCTL,
-                                AZX_PPCTL_PIE, 0);
-}
-EXPORT_SYMBOL_GPL(snd_hdac_ext_bus_ppcap_int_enable);
-
-/*
- * Multilink helpers - these helpers are useful for dealing with HDA
- * new multilink capability
- */
-
-/**
- * snd_hdac_ext_bus_get_ml_capabilities - get multilink capability
- * @bus: the pointer to HDAC bus object
- *
- * This will parse all links and read the mlink capabilities and add them
- * in hlink_list of extended hdac bus
- * Note: this will be freed on bus exit by driver
- */
-int snd_hdac_ext_bus_get_ml_capabilities(struct hdac_bus *bus)
-{
-       int idx;
-       u32 link_count;
-       struct hdac_ext_link *hlink;
-       u32 leptr;
-
-       link_count = readl(bus->mlcap + AZX_REG_ML_MLCD) + 1;
-
-       dev_dbg(bus->dev, "In %s Link count: %d\n", __func__, link_count);
-
-       for (idx = 0; idx < link_count; idx++) {
-               hlink  = kzalloc(sizeof(*hlink), GFP_KERNEL);
-               if (!hlink)
-                       return -ENOMEM;
-               hlink->index = idx;
-               hlink->bus = bus;
-               hlink->ml_addr = bus->mlcap + AZX_ML_BASE +
-                                       (AZX_ML_INTERVAL * idx);
-               hlink->lcaps  = readl(hlink->ml_addr + AZX_REG_ML_LCAP);
-               hlink->lsdiid = readw(hlink->ml_addr + AZX_REG_ML_LSDIID);
-               hlink->slcount = FIELD_GET(AZX_ML_HDA_LCAP_SLCOUNT, hlink->lcaps) + 1;
-
-               if (hdac_ext_link_alt(hlink)) {
-                       leptr = readl(hlink->ml_addr + AZX_REG_ML_LEPTR);
-                       hlink->id = FIELD_GET(AZX_REG_ML_LEPTR_ID, leptr);
-               }
-
-               /* since link in On, update the ref */
-               hlink->ref_count = 1;
-
-               list_add_tail(&hlink->list, &bus->hlink_list);
-       }
-
-       return 0;
-}
-EXPORT_SYMBOL_GPL(snd_hdac_ext_bus_get_ml_capabilities);
-
-/**
- * snd_hdac_ext_link_free_all- free hdac extended link objects
- *
- * @bus: the pointer to HDAC bus object
- */
-
-void snd_hdac_ext_link_free_all(struct hdac_bus *bus)
-{
-       struct hdac_ext_link *hlink;
-
-       while (!list_empty(&bus->hlink_list)) {
-               hlink = list_first_entry(&bus->hlink_list, struct hdac_ext_link, list);
-               list_del(&hlink->list);
-               kfree(hlink);
-       }
-}
-EXPORT_SYMBOL_GPL(snd_hdac_ext_link_free_all);
-
-struct hdac_ext_link *snd_hdac_ext_bus_get_hlink_by_id(struct hdac_bus *bus, u32 id)
-{
-       struct hdac_ext_link *hlink;
-
-       list_for_each_entry(hlink, &bus->hlink_list, list)
-               if (hdac_ext_link_alt(hlink) && hlink->id == id)
-                       return hlink;
-       return NULL;
-}
-EXPORT_SYMBOL_GPL(snd_hdac_ext_bus_get_hlink_by_id);
-
-/**
- * snd_hdac_ext_bus_get_hlink_by_addr - get hlink at specified address
- * @bus: hlink's parent bus device
- * @addr: codec device address
- *
- * Returns hlink object or NULL if matching hlink is not found.
- */
-struct hdac_ext_link *snd_hdac_ext_bus_get_hlink_by_addr(struct hdac_bus *bus, int addr)
-{
-       struct hdac_ext_link *hlink;
-
-       list_for_each_entry(hlink, &bus->hlink_list, list)
-               if (hlink->lsdiid & (0x1 << addr))
-                       return hlink;
-       return NULL;
-}
-EXPORT_SYMBOL_GPL(snd_hdac_ext_bus_get_hlink_by_addr);
-
-/**
- * snd_hdac_ext_bus_get_hlink_by_name - get hlink based on codec name
- * @bus: the pointer to HDAC bus object
- * @codec_name: codec name
- */
-struct hdac_ext_link *snd_hdac_ext_bus_get_hlink_by_name(struct hdac_bus *bus,
-                                                        const char *codec_name)
-{
-       int bus_idx, addr;
-
-       if (sscanf(codec_name, "ehdaudio%dD%d", &bus_idx, &addr) != 2)
-               return NULL;
-       if (bus->idx != bus_idx)
-               return NULL;
-       if (addr < 0 || addr > 31)
-               return NULL;
-
-       return snd_hdac_ext_bus_get_hlink_by_addr(bus, addr);
-}
-EXPORT_SYMBOL_GPL(snd_hdac_ext_bus_get_hlink_by_name);
-
-static int check_hdac_link_power_active(struct hdac_ext_link *hlink, bool enable)
-{
-       int timeout;
-       u32 val;
-       int mask = (1 << AZX_ML_LCTL_CPA_SHIFT);
-
-       udelay(3);
-       timeout = 150;
-
-       do {
-               val = readl(hlink->ml_addr + AZX_REG_ML_LCTL);
-               if (enable) {
-                       if (((val & mask) >> AZX_ML_LCTL_CPA_SHIFT))
-                               return 0;
-               } else {
-                       if (!((val & mask) >> AZX_ML_LCTL_CPA_SHIFT))
-                               return 0;
-               }
-               udelay(3);
-       } while (--timeout);
-
-       return -EIO;
-}
-
-/**
- * snd_hdac_ext_bus_link_power_up -power up hda link
- * @hlink: HD-audio extended link
- */
-int snd_hdac_ext_bus_link_power_up(struct hdac_ext_link *hlink)
-{
-       snd_hdac_updatel(hlink->ml_addr, AZX_REG_ML_LCTL,
-                        AZX_ML_LCTL_SPA, AZX_ML_LCTL_SPA);
-
-       return check_hdac_link_power_active(hlink, true);
-}
-EXPORT_SYMBOL_GPL(snd_hdac_ext_bus_link_power_up);
-
-/**
- * snd_hdac_ext_bus_link_power_down -power down hda link
- * @hlink: HD-audio extended link
- */
-int snd_hdac_ext_bus_link_power_down(struct hdac_ext_link *hlink)
-{
-       snd_hdac_updatel(hlink->ml_addr, AZX_REG_ML_LCTL, AZX_ML_LCTL_SPA, 0);
-
-       return check_hdac_link_power_active(hlink, false);
-}
-EXPORT_SYMBOL_GPL(snd_hdac_ext_bus_link_power_down);
-
-/**
- * snd_hdac_ext_bus_link_power_up_all -power up all hda link
- * @bus: the pointer to HDAC bus object
- */
-int snd_hdac_ext_bus_link_power_up_all(struct hdac_bus *bus)
-{
-       struct hdac_ext_link *hlink = NULL;
-       int ret;
-
-       list_for_each_entry(hlink, &bus->hlink_list, list) {
-               ret = snd_hdac_ext_bus_link_power_up(hlink);
-               if (ret < 0)
-                       return ret;
-       }
-
-       return 0;
-}
-EXPORT_SYMBOL_GPL(snd_hdac_ext_bus_link_power_up_all);
-
-/**
- * snd_hdac_ext_bus_link_power_down_all -power down all hda link
- * @bus: the pointer to HDAC bus object
- */
-int snd_hdac_ext_bus_link_power_down_all(struct hdac_bus *bus)
-{
-       struct hdac_ext_link *hlink = NULL;
-       int ret;
-
-       list_for_each_entry(hlink, &bus->hlink_list, list) {
-               ret = snd_hdac_ext_bus_link_power_down(hlink);
-               if (ret < 0)
-                       return ret;
-       }
-
-       return 0;
-}
-EXPORT_SYMBOL_GPL(snd_hdac_ext_bus_link_power_down_all);
-
-/**
- * snd_hdac_ext_bus_link_set_stream_id - maps stream id to link output
- * @link: HD-audio ext link to set up
- * @stream: stream id
- */
-void snd_hdac_ext_bus_link_set_stream_id(struct hdac_ext_link *link,
-                                        int stream)
-{
-       snd_hdac_updatew(link->ml_addr, AZX_REG_ML_LOSIDV, (1 << stream), 1 << stream);
-}
-EXPORT_SYMBOL_GPL(snd_hdac_ext_bus_link_set_stream_id);
-
-/**
- * snd_hdac_ext_bus_link_clear_stream_id - maps stream id to link output
- * @link: HD-audio ext link to set up
- * @stream: stream id
- */
-void snd_hdac_ext_bus_link_clear_stream_id(struct hdac_ext_link *link,
-                                          int stream)
-{
-       snd_hdac_updatew(link->ml_addr, AZX_REG_ML_LOSIDV, (1 << stream), 0);
-}
-EXPORT_SYMBOL_GPL(snd_hdac_ext_bus_link_clear_stream_id);
-
-int snd_hdac_ext_bus_link_get(struct hdac_bus *bus,
-                               struct hdac_ext_link *hlink)
-{
-       unsigned long codec_mask;
-       int ret = 0;
-
-       mutex_lock(&bus->lock);
-
-       /*
-        * if we move from 0 to 1, count will be 1 so power up this link
-        * as well, also check the dma status and trigger that
-        */
-       if (++hlink->ref_count == 1) {
-               if (!bus->cmd_dma_state) {
-                       snd_hdac_bus_init_cmd_io(bus);
-                       bus->cmd_dma_state = true;
-               }
-
-               ret = snd_hdac_ext_bus_link_power_up(hlink);
-
-               /*
-                * clear the register to invalidate all the output streams
-                */
-               snd_hdac_updatew(hlink->ml_addr, AZX_REG_ML_LOSIDV,
-                                AZX_ML_LOSIDV_STREAM_MASK, 0);
-               /*
-                *  wait for 521usec for codec to report status
-                *  HDA spec section 4.3 - Codec Discovery
-                */
-               udelay(521);
-               codec_mask = snd_hdac_chip_readw(bus, STATESTS);
-               dev_dbg(bus->dev, "codec_mask = 0x%lx\n", codec_mask);
-               snd_hdac_chip_writew(bus, STATESTS, codec_mask);
-               if (!bus->codec_mask)
-                       bus->codec_mask = codec_mask;
-       }
-
-       mutex_unlock(&bus->lock);
-       return ret;
-}
-EXPORT_SYMBOL_GPL(snd_hdac_ext_bus_link_get);
-
-int snd_hdac_ext_bus_link_put(struct hdac_bus *bus,
-                             struct hdac_ext_link *hlink)
-{
-       int ret = 0;
-       struct hdac_ext_link *hlink_tmp;
-       bool link_up = false;
-
-       mutex_lock(&bus->lock);
-
-       /*
-        * if we move from 1 to 0, count will be 0
-        * so power down this link as well
-        */
-       if (--hlink->ref_count == 0) {
-               ret = snd_hdac_ext_bus_link_power_down(hlink);
-
-               /*
-                * now check if all links are off, if so turn off
-                * cmd dma as well
-                */
-               list_for_each_entry(hlink_tmp, &bus->hlink_list, list) {
-                       if (hlink_tmp->ref_count) {
-                               link_up = true;
-                               break;
-                       }
-               }
-
-               if (!link_up) {
-                       snd_hdac_bus_stop_cmd_io(bus);
-                       bus->cmd_dma_state = false;
-               }
-       }
-
-       mutex_unlock(&bus->lock);
-       return ret;
-}
-EXPORT_SYMBOL_GPL(snd_hdac_ext_bus_link_put);
-
-static void hdac_ext_codec_link_up(struct hdac_device *codec)
-{
-       const char *devname = dev_name(&codec->dev);
-       struct hdac_ext_link *hlink =
-               snd_hdac_ext_bus_get_hlink_by_name(codec->bus, devname);
-
-       if (hlink)
-               snd_hdac_ext_bus_link_get(codec->bus, hlink);
-}
-
-static void hdac_ext_codec_link_down(struct hdac_device *codec)
-{
-       const char *devname = dev_name(&codec->dev);
-       struct hdac_ext_link *hlink =
-               snd_hdac_ext_bus_get_hlink_by_name(codec->bus, devname);
-
-       if (hlink)
-               snd_hdac_ext_bus_link_put(codec->bus, hlink);
-}
-
-void snd_hdac_ext_bus_link_power(struct hdac_device *codec, bool enable)
-{
-       struct hdac_bus *bus = codec->bus;
-       bool oldstate = test_bit(codec->addr, &bus->codec_powered);
-
-       if (enable == oldstate)
-               return;
-
-       snd_hdac_bus_link_power(codec, enable);
-
-       if (enable)
-               hdac_ext_codec_link_up(codec);
-       else
-               hdac_ext_codec_link_down(codec);
-}
-EXPORT_SYMBOL_GPL(snd_hdac_ext_bus_link_power);
diff --git a/sound/hda/ext/hdac_ext_stream.c b/sound/hda/ext/hdac_ext_stream.c
deleted file mode 100644 (file)
index a3ac738..0000000
+++ /dev/null
@@ -1,453 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0-only
-/*
- *  hdac-ext-stream.c - HD-audio extended stream operations.
- *
- *  Copyright (C) 2015 Intel Corp
- *  Author: Jeeja KP <jeeja.kp@intel.com>
- *  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- *
- * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- */
-
-#include <linux/delay.h>
-#include <linux/pci.h>
-#include <linux/pci_ids.h>
-#include <linux/slab.h>
-#include <sound/pcm.h>
-#include <sound/hda_register.h>
-#include <sound/hdaudio_ext.h>
-#include <sound/compress_driver.h>
-
-/**
- * snd_hdac_ext_host_stream_setup - Setup a HOST stream.
- * @hext_stream: HDAudio stream to set up.
- * @code_loading: Whether the stream is for PCM or code-loading.
- *
- * Return: Zero on success or negative error code.
- */
-int snd_hdac_ext_host_stream_setup(struct hdac_ext_stream *hext_stream, bool code_loading)
-{
-       return hext_stream->host_setup(hdac_stream(hext_stream), code_loading);
-}
-EXPORT_SYMBOL_GPL(snd_hdac_ext_host_stream_setup);
-
-/**
- * snd_hdac_apl_host_stream_setup - Setup a HOST stream following procedure
- *                                  recommended for ApolloLake devices.
- * @hstream: HDAudio stream to set up.
- * @code_loading: Whether the stream is for PCM or code-loading.
- *
- * Return: Zero on success or negative error code.
- */
-static int snd_hdac_apl_host_stream_setup(struct hdac_stream *hstream, bool code_loading)
-{
-       struct hdac_ext_stream *hext_stream = stream_to_hdac_ext_stream(hstream);
-       int ret;
-
-       snd_hdac_ext_stream_decouple(hstream->bus, hext_stream, false);
-       ret = snd_hdac_stream_setup(hstream, code_loading);
-       snd_hdac_ext_stream_decouple(hstream->bus, hext_stream, true);
-
-       return ret;
-}
-
-/**
- * snd_hdac_ext_stream_init - initialize each stream (aka device)
- * @bus: HD-audio core bus
- * @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
- *
- * initialize the stream, if ppcap is enabled then init those and then
- * invoke hdac stream initialization routine
- */
-static void snd_hdac_ext_stream_init(struct hdac_bus *bus,
-                                    struct hdac_ext_stream *hext_stream,
-                                    int idx, int direction, int tag)
-{
-       if (bus->ppcap) {
-               hext_stream->pphc_addr = bus->ppcap + AZX_PPHC_BASE +
-                               AZX_PPHC_INTERVAL * idx;
-
-               hext_stream->pplc_addr = bus->ppcap + AZX_PPLC_BASE +
-                               AZX_PPLC_MULTI * bus->num_streams +
-                               AZX_PPLC_INTERVAL * idx;
-       }
-
-       hext_stream->decoupled = false;
-       snd_hdac_stream_init(bus, &hext_stream->hstream, idx, direction, tag);
-}
-
-/**
- * snd_hdac_ext_stream_init_all - create and initialize the stream objects
- *   for an extended hda bus
- * @bus: HD-audio core bus
- * @start_idx: start index for streams
- * @num_stream: number of streams to initialize
- * @dir: direction of streams
- */
-int snd_hdac_ext_stream_init_all(struct hdac_bus *bus, int start_idx,
-                                int num_stream, int dir)
-{
-       struct pci_dev *pci = to_pci_dev(bus->dev);
-       int (*setup_op)(struct hdac_stream *, bool);
-       int stream_tag = 0;
-       int i, tag, idx = start_idx;
-
-       if (pci->device == PCI_DEVICE_ID_INTEL_HDA_APL)
-               setup_op = snd_hdac_apl_host_stream_setup;
-       else
-               setup_op = snd_hdac_stream_setup;
-
-       for (i = 0; i < num_stream; i++) {
-               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, hext_stream, idx, dir, tag);
-               idx++;
-               hext_stream->host_setup = setup_op;
-       }
-
-       return 0;
-
-}
-EXPORT_SYMBOL_GPL(snd_hdac_ext_stream_init_all);
-
-/**
- * snd_hdac_ext_stream_free_all - free hdac extended stream objects
- *
- * @bus: HD-audio core bus
- */
-void snd_hdac_ext_stream_free_all(struct hdac_bus *bus)
-{
-       struct hdac_stream *s, *_s;
-       struct hdac_ext_stream *hext_stream;
-
-       list_for_each_entry_safe(s, _s, &bus->stream_list, list) {
-               hext_stream = stream_to_hdac_ext_stream(s);
-               snd_hdac_ext_stream_decouple(bus, hext_stream, false);
-               list_del(&s->list);
-               kfree(hext_stream);
-       }
-}
-EXPORT_SYMBOL_GPL(snd_hdac_ext_stream_free_all);
-
-void snd_hdac_ext_stream_decouple_locked(struct hdac_bus *bus,
-                                        struct hdac_ext_stream *hext_stream,
-                                        bool decouple)
-{
-       struct hdac_stream *hstream = &hext_stream->hstream;
-       u32 val;
-       int mask = AZX_PPCTL_PROCEN(hstream->index);
-
-       val = readw(bus->ppcap + AZX_REG_PP_PPCTL) & mask;
-
-       if (decouple && !val)
-               snd_hdac_updatel(bus->ppcap, AZX_REG_PP_PPCTL, mask, mask);
-       else if (!decouple && val)
-               snd_hdac_updatel(bus->ppcap, AZX_REG_PP_PPCTL, mask, 0);
-
-       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
- * @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 *hext_stream, bool decouple)
-{
-       spin_lock_irq(&bus->reg_lock);
-       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_stream_start - start a stream
- * @hext_stream: HD-audio ext core stream to start
- */
-void snd_hdac_ext_stream_start(struct hdac_ext_stream *hext_stream)
-{
-       snd_hdac_updatel(hext_stream->pplc_addr, AZX_REG_PPLCCTL,
-                        AZX_PPLCCTL_RUN, AZX_PPLCCTL_RUN);
-}
-EXPORT_SYMBOL_GPL(snd_hdac_ext_stream_start);
-
-/**
- * snd_hdac_ext_stream_clear - stop a stream DMA
- * @hext_stream: HD-audio ext core stream to stop
- */
-void snd_hdac_ext_stream_clear(struct hdac_ext_stream *hext_stream)
-{
-       snd_hdac_updatel(hext_stream->pplc_addr, AZX_REG_PPLCCTL, AZX_PPLCCTL_RUN, 0);
-}
-EXPORT_SYMBOL_GPL(snd_hdac_ext_stream_clear);
-
-/**
- * snd_hdac_ext_stream_reset - reset a stream
- * @hext_stream: HD-audio ext core stream to reset
- */
-void snd_hdac_ext_stream_reset(struct hdac_ext_stream *hext_stream)
-{
-       unsigned char val;
-       int timeout;
-
-       snd_hdac_ext_stream_clear(hext_stream);
-
-       snd_hdac_updatel(hext_stream->pplc_addr, AZX_REG_PPLCCTL,
-                        AZX_PPLCCTL_STRST, AZX_PPLCCTL_STRST);
-       udelay(3);
-       timeout = 50;
-       do {
-               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, 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(hext_stream->pplc_addr + AZX_REG_PPLCCTL) & AZX_PPLCCTL_STRST;
-               if (!val)
-                       break;
-               udelay(3);
-       } while (--timeout);
-
-}
-EXPORT_SYMBOL_GPL(snd_hdac_ext_stream_reset);
-
-/**
- * snd_hdac_ext_stream_setup -  set up the SD for streaming
- * @hext_stream: HD-audio ext core stream to set up
- * @fmt: stream format
- */
-int snd_hdac_ext_stream_setup(struct hdac_ext_stream *hext_stream, int fmt)
-{
-       struct hdac_stream *hstream = &hext_stream->hstream;
-       unsigned int val;
-
-       /* make sure the run bit is zero for SD */
-       snd_hdac_ext_stream_clear(hext_stream);
-       /* program the stream_tag */
-       val = readl(hext_stream->pplc_addr + AZX_REG_PPLCCTL);
-       val = (val & ~AZX_PPLCCTL_STRM_MASK) |
-               (hstream->stream_tag << AZX_PPLCCTL_STRM_SHIFT);
-       writel(val, hext_stream->pplc_addr + AZX_REG_PPLCCTL);
-
-       /* program the stream format */
-       writew(fmt, hext_stream->pplc_addr + AZX_REG_PPLCFMT);
-
-       return 0;
-}
-EXPORT_SYMBOL_GPL(snd_hdac_ext_stream_setup);
-
-static struct hdac_ext_stream *
-hdac_ext_link_dma_stream_assign(struct hdac_bus *bus,
-                               struct snd_pcm_substream *substream)
-{
-       struct hdac_ext_stream *res = NULL;
-       struct hdac_stream *hstream = NULL;
-
-       if (!bus->ppcap) {
-               dev_err(bus->dev, "stream type not supported\n");
-               return NULL;
-       }
-
-       spin_lock_irq(&bus->reg_lock);
-       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 link stream is available */
-               if (!hext_stream->link_locked) {
-                       res = hext_stream;
-                       break;
-               }
-
-       }
-       if (res) {
-               snd_hdac_ext_stream_decouple_locked(bus, res, true);
-               res->link_locked = 1;
-               res->link_substream = substream;
-       }
-       spin_unlock_irq(&bus->reg_lock);
-       return res;
-}
-
-static struct hdac_ext_stream *
-hdac_ext_host_dma_stream_assign(struct hdac_bus *bus,
-                               struct snd_pcm_substream *substream)
-{
-       struct hdac_ext_stream *res = NULL;
-       struct hdac_stream *hstream = NULL;
-
-       if (!bus->ppcap) {
-               dev_err(bus->dev, "stream type not supported\n");
-               return NULL;
-       }
-
-       spin_lock_irq(&bus->reg_lock);
-       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 (!hstream->opened) {
-                       res = hext_stream;
-                       break;
-               }
-       }
-       if (res) {
-               snd_hdac_ext_stream_decouple_locked(bus, res, true);
-               res->hstream.opened = 1;
-               res->hstream.running = 0;
-               res->hstream.substream = substream;
-       }
-       spin_unlock_irq(&bus->reg_lock);
-
-       return res;
-}
-
-/**
- * snd_hdac_ext_stream_assign - assign a stream for the PCM
- * @bus: HD-audio core bus
- * @substream: PCM substream to assign
- * @type: type of stream (coupled, host or link stream)
- *
- * This assigns the stream based on the type (coupled/host/link), for the
- * given PCM substream, assigns it and returns the stream object
- *
- * coupled: Looks for an unused stream
- * host: Looks for an unused decoupled host stream
- * link: Looks for an unused decoupled link stream
- *
- * If no stream is free, returns NULL. The function tries to keep using
- * the same stream object when it's used beforehand.  when a stream is
- * decoupled, it becomes a host stream and link stream.
- */
-struct hdac_ext_stream *snd_hdac_ext_stream_assign(struct hdac_bus *bus,
-                                          struct snd_pcm_substream *substream,
-                                          int type)
-{
-       struct hdac_ext_stream *hext_stream = NULL;
-       struct hdac_stream *hstream = NULL;
-
-       switch (type) {
-       case HDAC_EXT_STREAM_TYPE_COUPLED:
-               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_dma_stream_assign(bus, substream);
-
-       case HDAC_EXT_STREAM_TYPE_LINK:
-               return hdac_ext_link_dma_stream_assign(bus, substream);
-
-       default:
-               return NULL;
-       }
-}
-EXPORT_SYMBOL_GPL(snd_hdac_ext_stream_assign);
-
-/**
- * snd_hdac_ext_stream_release - release the assigned stream
- * @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 *hext_stream, int type)
-{
-       struct hdac_bus *bus = hext_stream->hstream.bus;
-
-       switch (type) {
-       case HDAC_EXT_STREAM_TYPE_COUPLED:
-               snd_hdac_stream_release(&hext_stream->hstream);
-               break;
-
-       case HDAC_EXT_STREAM_TYPE_HOST:
-               spin_lock_irq(&bus->reg_lock);
-               /* couple link only if not in use */
-               if (!hext_stream->link_locked)
-                       snd_hdac_ext_stream_decouple_locked(bus, hext_stream, false);
-               snd_hdac_stream_release_locked(&hext_stream->hstream);
-               spin_unlock_irq(&bus->reg_lock);
-               break;
-
-       case HDAC_EXT_STREAM_TYPE_LINK:
-               spin_lock_irq(&bus->reg_lock);
-               /* couple host only if not in use */
-               if (!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;
-
-       default:
-               dev_dbg(bus->dev, "Invalid type %d\n", type);
-       }
-
-}
-EXPORT_SYMBOL_GPL(snd_hdac_ext_stream_release);
-
-/**
- * snd_hdac_ext_cstream_assign - assign a host stream for compress
- * @bus: HD-audio core bus
- * @cstream: Compress stream to assign
- *
- * Assign an unused host stream for the given compress stream.
- * If no stream is free, NULL is returned. Stream is decoupled
- * before assignment.
- */
-struct hdac_ext_stream *snd_hdac_ext_cstream_assign(struct hdac_bus *bus,
-                                                   struct snd_compr_stream *cstream)
-{
-       struct hdac_ext_stream *res = NULL;
-       struct hdac_stream *hstream;
-
-       spin_lock_irq(&bus->reg_lock);
-       list_for_each_entry(hstream, &bus->stream_list, list) {
-               struct hdac_ext_stream *hext_stream = stream_to_hdac_ext_stream(hstream);
-
-               if (hstream->direction != cstream->direction)
-                       continue;
-
-               if (!hstream->opened) {
-                       res = hext_stream;
-                       break;
-               }
-       }
-
-       if (res) {
-               snd_hdac_ext_stream_decouple_locked(bus, res, true);
-               res->hstream.opened = 1;
-               res->hstream.running = 0;
-               res->hstream.cstream = cstream;
-       }
-       spin_unlock_irq(&bus->reg_lock);
-
-       return res;
-}
-EXPORT_SYMBOL_GPL(snd_hdac_ext_cstream_assign);
diff --git a/sound/hda/hda_bus_type.c b/sound/hda/hda_bus_type.c
deleted file mode 100644 (file)
index eb72a7a..0000000
+++ /dev/null
@@ -1,97 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0-only
-/*
- * HD-audio bus
- */
-#include <linux/init.h>
-#include <linux/device.h>
-#include <linux/module.h>
-#include <linux/mod_devicetable.h>
-#include <linux/export.h>
-#include <sound/hdaudio.h>
-
-MODULE_DESCRIPTION("HD-audio bus");
-MODULE_LICENSE("GPL");
-
-/**
- * hdac_get_device_id - gets the hdac device id entry
- * @hdev: HD-audio core device
- * @drv: HD-audio codec driver
- *
- * Compares the hdac device vendor_id and revision_id to the hdac_device
- * driver id_table and returns the matching device id entry.
- */
-const struct hda_device_id *
-hdac_get_device_id(struct hdac_device *hdev, const struct hdac_driver *drv)
-{
-       if (drv->id_table) {
-               const struct hda_device_id *id  = drv->id_table;
-
-               while (id->vendor_id) {
-                       if (hdev->vendor_id == id->vendor_id &&
-                               (!id->rev_id || id->rev_id == hdev->revision_id))
-                               return id;
-                       id++;
-               }
-       }
-
-       return NULL;
-}
-EXPORT_SYMBOL_GPL(hdac_get_device_id);
-
-static int hdac_codec_match(struct hdac_device *dev, const struct hdac_driver *drv)
-{
-       if (hdac_get_device_id(dev, drv))
-               return 1;
-       else
-               return 0;
-}
-
-static int hda_bus_match(struct device *dev, const struct device_driver *drv)
-{
-       struct hdac_device *hdev = dev_to_hdac_dev(dev);
-       const struct hdac_driver *hdrv = drv_to_hdac_driver(drv);
-
-       if (hdev->type != hdrv->type)
-               return 0;
-
-       /*
-        * if driver provided a match function use that otherwise we will
-        * use hdac_codec_match function
-        */
-       if (hdrv->match)
-               return hdrv->match(hdev, hdrv);
-       else
-               return hdac_codec_match(hdev, hdrv);
-       return 1;
-}
-
-static int hda_uevent(const struct device *dev, struct kobj_uevent_env *env)
-{
-       char modalias[32];
-
-       snd_hdac_codec_modalias(dev_to_hdac_dev(dev), modalias,
-                               sizeof(modalias));
-       if (add_uevent_var(env, "MODALIAS=%s", modalias))
-               return -ENOMEM;
-       return 0;
-}
-
-const struct bus_type snd_hda_bus_type = {
-       .name = "hdaudio",
-       .match = hda_bus_match,
-       .uevent = hda_uevent,
-};
-EXPORT_SYMBOL_GPL(snd_hda_bus_type);
-
-static int __init hda_bus_init(void)
-{
-       return bus_register(&snd_hda_bus_type);
-}
-
-static void __exit hda_bus_exit(void)
-{
-       bus_unregister(&snd_hda_bus_type);
-}
-
-subsys_initcall(hda_bus_init);
-module_exit(hda_bus_exit);
diff --git a/sound/hda/hdac_bus.c b/sound/hda/hdac_bus.c
deleted file mode 100644 (file)
index d497414..0000000
+++ /dev/null
@@ -1,289 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0-only
-/*
- * HD-audio core bus driver
- */
-
-#include <linux/init.h>
-#include <linux/io.h>
-#include <linux/device.h>
-#include <linux/module.h>
-#include <linux/export.h>
-#include <sound/hdaudio.h>
-#include "local.h"
-#include "trace.h"
-
-static void snd_hdac_bus_process_unsol_events(struct work_struct *work);
-
-static const struct hdac_bus_ops default_ops = {
-       .command = snd_hdac_bus_send_cmd,
-       .get_response = snd_hdac_bus_get_response,
-       .link_power = snd_hdac_bus_link_power,
-};
-
-/**
- * snd_hdac_bus_init - initialize a HD-audio bas bus
- * @bus: the pointer to bus object
- * @dev: device pointer
- * @ops: bus verb operators
- *
- * Returns 0 if successful, or a negative error code.
- */
-int snd_hdac_bus_init(struct hdac_bus *bus, struct device *dev,
-                     const struct hdac_bus_ops *ops)
-{
-       memset(bus, 0, sizeof(*bus));
-       bus->dev = dev;
-       if (ops)
-               bus->ops = ops;
-       else
-               bus->ops = &default_ops;
-       bus->dma_type = SNDRV_DMA_TYPE_DEV;
-       INIT_LIST_HEAD(&bus->stream_list);
-       INIT_LIST_HEAD(&bus->codec_list);
-       INIT_WORK(&bus->unsol_work, snd_hdac_bus_process_unsol_events);
-       spin_lock_init(&bus->reg_lock);
-       mutex_init(&bus->cmd_mutex);
-       mutex_init(&bus->lock);
-       INIT_LIST_HEAD(&bus->hlink_list);
-       init_waitqueue_head(&bus->rirb_wq);
-       bus->irq = -1;
-
-       /*
-        * Default value of '8' is as per the HD audio specification (Rev 1.0a).
-        * Following relation is used to derive STRIPE control value.
-        *  For sample rate <= 48K:
-        *   { ((num_channels * bits_per_sample) / number of SDOs) >= 8 }
-        *  For sample rate > 48K:
-        *   { ((num_channels * bits_per_sample * rate/48000) /
-        *      number of SDOs) >= 8 }
-        */
-       bus->sdo_limit = 8;
-
-       return 0;
-}
-EXPORT_SYMBOL_GPL(snd_hdac_bus_init);
-
-/**
- * snd_hdac_bus_exit - clean up a HD-audio bas bus
- * @bus: the pointer to bus object
- */
-void snd_hdac_bus_exit(struct hdac_bus *bus)
-{
-       WARN_ON(!list_empty(&bus->stream_list));
-       WARN_ON(!list_empty(&bus->codec_list));
-       cancel_work_sync(&bus->unsol_work);
-}
-EXPORT_SYMBOL_GPL(snd_hdac_bus_exit);
-
-/**
- * snd_hdac_bus_exec_verb - execute a HD-audio verb on the given bus
- * @bus: bus object
- * @addr: the HDAC device address
- * @cmd: HD-audio encoded verb
- * @res: pointer to store the response, NULL if performing asynchronously
- *
- * Returns 0 if successful, or a negative error code.
- */
-int snd_hdac_bus_exec_verb(struct hdac_bus *bus, unsigned int addr,
-                          unsigned int cmd, unsigned int *res)
-{
-       int err;
-
-       mutex_lock(&bus->cmd_mutex);
-       err = snd_hdac_bus_exec_verb_unlocked(bus, addr, cmd, res);
-       mutex_unlock(&bus->cmd_mutex);
-       return err;
-}
-
-/**
- * snd_hdac_bus_exec_verb_unlocked - unlocked version
- * @bus: bus object
- * @addr: the HDAC device address
- * @cmd: HD-audio encoded verb
- * @res: pointer to store the response, NULL if performing asynchronously
- *
- * Returns 0 if successful, or a negative error code.
- */
-int snd_hdac_bus_exec_verb_unlocked(struct hdac_bus *bus, unsigned int addr,
-                                   unsigned int cmd, unsigned int *res)
-{
-       unsigned int tmp;
-       int err;
-
-       if (cmd == ~0)
-               return -EINVAL;
-
-       if (res)
-               *res = -1;
-       else if (bus->sync_write)
-               res = &tmp;
-       for (;;) {
-               trace_hda_send_cmd(bus, cmd);
-               err = bus->ops->command(bus, cmd);
-               if (err != -EAGAIN)
-                       break;
-               /* process pending verbs */
-               err = bus->ops->get_response(bus, addr, &tmp);
-               if (err)
-                       break;
-       }
-       if (!err && res) {
-               err = bus->ops->get_response(bus, addr, res);
-               trace_hda_get_response(bus, addr, *res);
-       }
-       return err;
-}
-EXPORT_SYMBOL_GPL(snd_hdac_bus_exec_verb_unlocked);
-
-/**
- * snd_hdac_bus_queue_event - add an unsolicited event to queue
- * @bus: the BUS
- * @res: unsolicited event (lower 32bit of RIRB entry)
- * @res_ex: codec addr and flags (upper 32bit or RIRB entry)
- *
- * Adds the given event to the queue.  The events are processed in
- * the workqueue asynchronously.  Call this function in the interrupt
- * hanlder when RIRB receives an unsolicited event.
- */
-void snd_hdac_bus_queue_event(struct hdac_bus *bus, u32 res, u32 res_ex)
-{
-       unsigned int wp;
-
-       if (!bus)
-               return;
-
-       trace_hda_unsol_event(bus, res, res_ex);
-       wp = (bus->unsol_wp + 1) % HDA_UNSOL_QUEUE_SIZE;
-       bus->unsol_wp = wp;
-
-       wp <<= 1;
-       bus->unsol_queue[wp] = res;
-       bus->unsol_queue[wp + 1] = res_ex;
-
-       schedule_work(&bus->unsol_work);
-}
-
-/*
- * process queued unsolicited events
- */
-static void snd_hdac_bus_process_unsol_events(struct work_struct *work)
-{
-       struct hdac_bus *bus = container_of(work, struct hdac_bus, unsol_work);
-       struct hdac_device *codec;
-       struct hdac_driver *drv;
-       unsigned int rp, caddr, res;
-
-       spin_lock_irq(&bus->reg_lock);
-       while (bus->unsol_rp != bus->unsol_wp) {
-               rp = (bus->unsol_rp + 1) % HDA_UNSOL_QUEUE_SIZE;
-               bus->unsol_rp = rp;
-               rp <<= 1;
-               res = bus->unsol_queue[rp];
-               caddr = bus->unsol_queue[rp + 1];
-               if (!(caddr & (1 << 4))) /* no unsolicited event? */
-                       continue;
-               codec = bus->caddr_tbl[caddr & 0x0f];
-               if (!codec || !codec->registered)
-                       continue;
-               spin_unlock_irq(&bus->reg_lock);
-               drv = drv_to_hdac_driver(codec->dev.driver);
-               if (drv->unsol_event)
-                       drv->unsol_event(codec, res);
-               spin_lock_irq(&bus->reg_lock);
-       }
-       spin_unlock_irq(&bus->reg_lock);
-}
-
-/**
- * snd_hdac_bus_add_device - Add a codec to bus
- * @bus: HDA core bus
- * @codec: HDA core device to add
- *
- * Adds the given codec to the list in the bus.  The caddr_tbl array
- * and codec_powered bits are updated, as well.
- * Returns zero if success, or a negative error code.
- */
-int snd_hdac_bus_add_device(struct hdac_bus *bus, struct hdac_device *codec)
-{
-       if (bus->caddr_tbl[codec->addr]) {
-               dev_err(bus->dev, "address 0x%x is already occupied\n",
-                       codec->addr);
-               return -EBUSY;
-       }
-
-       list_add_tail(&codec->list, &bus->codec_list);
-       bus->caddr_tbl[codec->addr] = codec;
-       set_bit(codec->addr, &bus->codec_powered);
-       bus->num_codecs++;
-       return 0;
-}
-
-/**
- * snd_hdac_bus_remove_device - Remove a codec from bus
- * @bus: HDA core bus
- * @codec: HDA core device to remove
- */
-void snd_hdac_bus_remove_device(struct hdac_bus *bus,
-                               struct hdac_device *codec)
-{
-       WARN_ON(bus != codec->bus);
-       if (list_empty(&codec->list))
-               return;
-       list_del_init(&codec->list);
-       bus->caddr_tbl[codec->addr] = NULL;
-       clear_bit(codec->addr, &bus->codec_powered);
-       bus->num_codecs--;
-       flush_work(&bus->unsol_work);
-}
-
-#ifdef CONFIG_SND_HDA_ALIGNED_MMIO
-/* Helpers for aligned read/write of mmio space, for Tegra */
-unsigned int snd_hdac_aligned_read(void __iomem *addr, unsigned int mask)
-{
-       void __iomem *aligned_addr =
-               (void __iomem *)((unsigned long)(addr) & ~0x3);
-       unsigned int shift = ((unsigned long)(addr) & 0x3) << 3;
-       unsigned int v;
-
-       v = readl(aligned_addr);
-       return (v >> shift) & mask;
-}
-EXPORT_SYMBOL_GPL(snd_hdac_aligned_read);
-
-void snd_hdac_aligned_write(unsigned int val, void __iomem *addr,
-                           unsigned int mask)
-{
-       void __iomem *aligned_addr =
-               (void __iomem *)((unsigned long)(addr) & ~0x3);
-       unsigned int shift = ((unsigned long)(addr) & 0x3) << 3;
-       unsigned int v;
-
-       v = readl(aligned_addr);
-       v &= ~(mask << shift);
-       v |= val << shift;
-       writel(v, aligned_addr);
-}
-EXPORT_SYMBOL_GPL(snd_hdac_aligned_write);
-#endif /* CONFIG_SND_HDA_ALIGNED_MMIO */
-
-void snd_hdac_codec_link_up(struct hdac_device *codec)
-{
-       struct hdac_bus *bus = codec->bus;
-
-       if (bus->ops->link_power)
-               bus->ops->link_power(codec, true);
-       else
-               snd_hdac_bus_link_power(codec, true);
-}
-EXPORT_SYMBOL_GPL(snd_hdac_codec_link_up);
-
-void snd_hdac_codec_link_down(struct hdac_device *codec)
-{
-       struct hdac_bus *bus = codec->bus;
-
-       if (bus->ops->link_power)
-               bus->ops->link_power(codec, false);
-       else
-               snd_hdac_bus_link_power(codec, false);
-}
-EXPORT_SYMBOL_GPL(snd_hdac_codec_link_down);
diff --git a/sound/hda/hdac_component.c b/sound/hda/hdac_component.c
deleted file mode 100644 (file)
index 9c82a28..0000000
+++ /dev/null
@@ -1,353 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0
-// hdac_component.c - routines for sync between HD-A core and DRM driver
-
-#include <linux/init.h>
-#include <linux/module.h>
-#include <linux/pci.h>
-#include <linux/component.h>
-#include <linux/string_choices.h>
-#include <sound/core.h>
-#include <sound/hdaudio.h>
-#include <sound/hda_component.h>
-#include <sound/hda_register.h>
-
-static void hdac_acomp_release(struct device *dev, void *res)
-{
-}
-
-static struct drm_audio_component *hdac_get_acomp(struct device *dev)
-{
-       return devres_find(dev, hdac_acomp_release, NULL, NULL);
-}
-
-/**
- * snd_hdac_set_codec_wakeup - Enable / disable HDMI/DP codec wakeup
- * @bus: HDA core bus
- * @enable: enable or disable the wakeup
- *
- * This function is supposed to be used only by a HD-audio controller
- * driver that needs the interaction with graphics driver.
- *
- * This function should be called during the chip reset, also called at
- * resume for updating STATESTS register read.
- *
- * Returns zero for success or a negative error code.
- */
-int snd_hdac_set_codec_wakeup(struct hdac_bus *bus, bool enable)
-{
-       struct drm_audio_component *acomp = bus->audio_component;
-
-       if (!acomp || !acomp->ops)
-               return -ENODEV;
-
-       if (!acomp->ops->codec_wake_override)
-               return 0;
-
-       dev_dbg(bus->dev, "%s codec wakeup\n", str_enable_disable(enable));
-
-       acomp->ops->codec_wake_override(acomp->dev, enable);
-
-       return 0;
-}
-EXPORT_SYMBOL_GPL(snd_hdac_set_codec_wakeup);
-
-/**
- * snd_hdac_display_power - Power up / down the power refcount
- * @bus: HDA core bus
- * @idx: HDA codec address, pass HDA_CODEC_IDX_CONTROLLER for controller
- * @enable: power up or down
- *
- * This function is used by either HD-audio controller or codec driver that
- * needs the interaction with graphics driver.
- *
- * This function updates the power status, and calls the get_power() and
- * put_power() ops accordingly, toggling the codec wakeup, too.
- */
-void snd_hdac_display_power(struct hdac_bus *bus, unsigned int idx, bool enable)
-{
-       struct drm_audio_component *acomp = bus->audio_component;
-
-       dev_dbg(bus->dev, "display power %s\n", str_enable_disable(enable));
-
-       mutex_lock(&bus->lock);
-       if (enable)
-               set_bit(idx, &bus->display_power_status);
-       else
-               clear_bit(idx, &bus->display_power_status);
-
-       if (!acomp || !acomp->ops)
-               goto unlock;
-
-       if (bus->display_power_status) {
-               if (!bus->display_power_active) {
-                       unsigned long cookie = -1;
-
-                       if (acomp->ops->get_power)
-                               cookie = acomp->ops->get_power(acomp->dev);
-
-                       snd_hdac_set_codec_wakeup(bus, true);
-                       snd_hdac_set_codec_wakeup(bus, false);
-                       bus->display_power_active = cookie;
-               }
-       } else {
-               if (bus->display_power_active) {
-                       unsigned long cookie = bus->display_power_active;
-
-                       if (acomp->ops->put_power)
-                               acomp->ops->put_power(acomp->dev, cookie);
-
-                       bus->display_power_active = 0;
-               }
-       }
- unlock:
-       mutex_unlock(&bus->lock);
-}
-EXPORT_SYMBOL_GPL(snd_hdac_display_power);
-
-/**
- * snd_hdac_sync_audio_rate - Set N/CTS based on the sample rate
- * @codec: HDA codec
- * @nid: the pin widget NID
- * @dev_id: device identifier
- * @rate: the sample rate to set
- *
- * This function is supposed to be used only by a HD-audio controller
- * driver that needs the interaction with graphics driver.
- *
- * This function sets N/CTS value based on the given sample rate.
- * Returns zero for success, or a negative error code.
- */
-int snd_hdac_sync_audio_rate(struct hdac_device *codec, hda_nid_t nid,
-                            int dev_id, int rate)
-{
-       struct hdac_bus *bus = codec->bus;
-       struct drm_audio_component *acomp = bus->audio_component;
-       int port, pipe;
-
-       if (!acomp || !acomp->ops || !acomp->ops->sync_audio_rate)
-               return -ENODEV;
-       port = nid;
-       if (acomp->audio_ops && acomp->audio_ops->pin2port) {
-               port = acomp->audio_ops->pin2port(codec, nid);
-               if (port < 0)
-                       return -EINVAL;
-       }
-       pipe = dev_id;
-       return acomp->ops->sync_audio_rate(acomp->dev, port, pipe, rate);
-}
-EXPORT_SYMBOL_GPL(snd_hdac_sync_audio_rate);
-
-/**
- * snd_hdac_acomp_get_eld - Get the audio state and ELD via component
- * @codec: HDA codec
- * @nid: the pin widget NID
- * @dev_id: device identifier
- * @audio_enabled: the pointer to store the current audio state
- * @buffer: the buffer pointer to store ELD bytes
- * @max_bytes: the max bytes to be stored on @buffer
- *
- * This function is supposed to be used only by a HD-audio controller
- * driver that needs the interaction with graphics driver.
- *
- * This function queries the current state of the audio on the given
- * digital port and fetches the ELD bytes onto the given buffer.
- * It returns the number of bytes for the total ELD data, zero for
- * invalid ELD, or a negative error code.
- *
- * The return size is the total bytes required for the whole ELD bytes,
- * thus it may be over @max_bytes.  If it's over @max_bytes, it implies
- * that only a part of ELD bytes have been fetched.
- */
-int snd_hdac_acomp_get_eld(struct hdac_device *codec, hda_nid_t nid, int dev_id,
-                          bool *audio_enabled, char *buffer, int max_bytes)
-{
-       struct hdac_bus *bus = codec->bus;
-       struct drm_audio_component *acomp = bus->audio_component;
-       int port, pipe;
-
-       if (!acomp || !acomp->ops || !acomp->ops->get_eld)
-               return -ENODEV;
-
-       port = nid;
-       if (acomp->audio_ops && acomp->audio_ops->pin2port) {
-               port = acomp->audio_ops->pin2port(codec, nid);
-               if (port < 0)
-                       return -EINVAL;
-       }
-       pipe = dev_id;
-       return acomp->ops->get_eld(acomp->dev, port, pipe, audio_enabled,
-                                  buffer, max_bytes);
-}
-EXPORT_SYMBOL_GPL(snd_hdac_acomp_get_eld);
-
-static int hdac_component_master_bind(struct device *dev)
-{
-       struct drm_audio_component *acomp = hdac_get_acomp(dev);
-       int ret;
-
-       if (WARN_ON(!acomp))
-               return -EINVAL;
-
-       ret = component_bind_all(dev, acomp);
-       if (ret < 0)
-               return ret;
-
-       if (WARN_ON(!(acomp->dev && acomp->ops))) {
-               ret = -EINVAL;
-               goto out_unbind;
-       }
-
-       /* pin the module to avoid dynamic unbinding, but only if given */
-       if (!try_module_get(acomp->ops->owner)) {
-               ret = -ENODEV;
-               goto out_unbind;
-       }
-
-       if (acomp->audio_ops && acomp->audio_ops->master_bind) {
-               ret = acomp->audio_ops->master_bind(dev, acomp);
-               if (ret < 0)
-                       goto module_put;
-       }
-
-       complete_all(&acomp->master_bind_complete);
-       return 0;
-
- module_put:
-       module_put(acomp->ops->owner);
-out_unbind:
-       component_unbind_all(dev, acomp);
-       complete_all(&acomp->master_bind_complete);
-
-       return ret;
-}
-
-static void hdac_component_master_unbind(struct device *dev)
-{
-       struct drm_audio_component *acomp = hdac_get_acomp(dev);
-
-       if (acomp->audio_ops && acomp->audio_ops->master_unbind)
-               acomp->audio_ops->master_unbind(dev, acomp);
-       module_put(acomp->ops->owner);
-       component_unbind_all(dev, acomp);
-       WARN_ON(acomp->ops || acomp->dev);
-}
-
-static const struct component_master_ops hdac_component_master_ops = {
-       .bind = hdac_component_master_bind,
-       .unbind = hdac_component_master_unbind,
-};
-
-/**
- * snd_hdac_acomp_register_notifier - Register audio component ops
- * @bus: HDA core bus
- * @aops: audio component ops
- *
- * This function is supposed to be used only by a HD-audio controller
- * driver that needs the interaction with graphics driver.
- *
- * This function sets the given ops to be called by the graphics driver.
- *
- * Returns zero for success or a negative error code.
- */
-int snd_hdac_acomp_register_notifier(struct hdac_bus *bus,
-                                   const struct drm_audio_component_audio_ops *aops)
-{
-       if (!bus->audio_component)
-               return -ENODEV;
-
-       bus->audio_component->audio_ops = aops;
-       return 0;
-}
-EXPORT_SYMBOL_GPL(snd_hdac_acomp_register_notifier);
-
-/**
- * snd_hdac_acomp_init - Initialize audio component
- * @bus: HDA core bus
- * @aops: audio component ops
- * @match_master: match function for finding components
- * @extra_size: Extra bytes to allocate
- *
- * This function is supposed to be used only by a HD-audio controller
- * driver that needs the interaction with graphics driver.
- *
- * This function initializes and sets up the audio component to communicate
- * with graphics driver.
- *
- * Unlike snd_hdac_i915_init(), this function doesn't synchronize with the
- * binding with the DRM component.  Each caller needs to sync via master_bind
- * audio_ops.
- *
- * Returns zero for success or a negative error code.
- */
-int snd_hdac_acomp_init(struct hdac_bus *bus,
-                       const struct drm_audio_component_audio_ops *aops,
-                       int (*match_master)(struct device *, int, void *),
-                       size_t extra_size)
-{
-       struct component_match *match = NULL;
-       struct device *dev = bus->dev;
-       struct drm_audio_component *acomp;
-       int ret;
-
-       if (WARN_ON(hdac_get_acomp(dev)))
-               return -EBUSY;
-
-       acomp = devres_alloc(hdac_acomp_release, sizeof(*acomp) + extra_size,
-                            GFP_KERNEL);
-       if (!acomp)
-               return -ENOMEM;
-       acomp->audio_ops = aops;
-       init_completion(&acomp->master_bind_complete);
-       bus->audio_component = acomp;
-       devres_add(dev, acomp);
-
-       component_match_add_typed(dev, &match, match_master, bus);
-       ret = component_master_add_with_match(dev, &hdac_component_master_ops,
-                                             match);
-       if (ret < 0)
-               goto out_err;
-
-       return 0;
-
-out_err:
-       bus->audio_component = NULL;
-       devres_destroy(dev, hdac_acomp_release, NULL, NULL);
-       dev_info(dev, "failed to add audio component master (%d)\n", ret);
-
-       return ret;
-}
-EXPORT_SYMBOL_GPL(snd_hdac_acomp_init);
-
-/**
- * snd_hdac_acomp_exit - Finalize audio component
- * @bus: HDA core bus
- *
- * This function is supposed to be used only by a HD-audio controller
- * driver that needs the interaction with graphics driver.
- *
- * This function releases the audio component that has been used.
- *
- * Returns zero for success or a negative error code.
- */
-int snd_hdac_acomp_exit(struct hdac_bus *bus)
-{
-       struct device *dev = bus->dev;
-       struct drm_audio_component *acomp = bus->audio_component;
-
-       if (!acomp)
-               return 0;
-
-       if (WARN_ON(bus->display_power_active) && acomp->ops)
-               acomp->ops->put_power(acomp->dev, bus->display_power_active);
-
-       bus->display_power_active = 0;
-       bus->display_power_status = 0;
-
-       component_master_del(dev, &hdac_component_master_ops);
-
-       bus->audio_component = NULL;
-       devres_destroy(dev, hdac_acomp_release, NULL, NULL);
-
-       return 0;
-}
-EXPORT_SYMBOL_GPL(snd_hdac_acomp_exit);
diff --git a/sound/hda/hdac_controller.c b/sound/hda/hdac_controller.c
deleted file mode 100644 (file)
index b5c833b..0000000
+++ /dev/null
@@ -1,778 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0-only
-/*
- * HD-audio controller helpers
- */
-
-#include <linux/kernel.h>
-#include <linux/delay.h>
-#include <linux/export.h>
-#include <sound/core.h>
-#include <sound/hdaudio.h>
-#include <sound/hda_register.h>
-#include "local.h"
-
-/* clear CORB read pointer properly */
-static void azx_clear_corbrp(struct hdac_bus *bus)
-{
-       int timeout;
-
-       for (timeout = 1000; timeout > 0; timeout--) {
-               if (snd_hdac_chip_readw(bus, CORBRP) & AZX_CORBRP_RST)
-                       break;
-               udelay(1);
-       }
-       if (timeout <= 0)
-               dev_err(bus->dev, "CORB reset timeout#1, CORBRP = %d\n",
-                       snd_hdac_chip_readw(bus, CORBRP));
-
-       snd_hdac_chip_writew(bus, CORBRP, 0);
-       for (timeout = 1000; timeout > 0; timeout--) {
-               if (snd_hdac_chip_readw(bus, CORBRP) == 0)
-                       break;
-               udelay(1);
-       }
-       if (timeout <= 0)
-               dev_err(bus->dev, "CORB reset timeout#2, CORBRP = %d\n",
-                       snd_hdac_chip_readw(bus, CORBRP));
-}
-
-/**
- * snd_hdac_bus_init_cmd_io - set up CORB/RIRB buffers
- * @bus: HD-audio core bus
- */
-void snd_hdac_bus_init_cmd_io(struct hdac_bus *bus)
-{
-       WARN_ON_ONCE(!bus->rb.area);
-
-       spin_lock_irq(&bus->reg_lock);
-       /* CORB set up */
-       bus->corb.addr = bus->rb.addr;
-       bus->corb.buf = (__le32 *)bus->rb.area;
-       snd_hdac_chip_writel(bus, CORBLBASE, (u32)bus->corb.addr);
-       snd_hdac_chip_writel(bus, CORBUBASE, upper_32_bits(bus->corb.addr));
-
-       /* set the corb size to 256 entries (ULI requires explicitly) */
-       snd_hdac_chip_writeb(bus, CORBSIZE, 0x02);
-       /* set the corb write pointer to 0 */
-       snd_hdac_chip_writew(bus, CORBWP, 0);
-
-       /* reset the corb hw read pointer */
-       snd_hdac_chip_writew(bus, CORBRP, AZX_CORBRP_RST);
-       if (!bus->corbrp_self_clear)
-               azx_clear_corbrp(bus);
-
-       /* enable corb dma */
-       if (!bus->use_pio_for_commands)
-               snd_hdac_chip_writeb(bus, CORBCTL, AZX_CORBCTL_RUN);
-
-       /* RIRB set up */
-       bus->rirb.addr = bus->rb.addr + 2048;
-       bus->rirb.buf = (__le32 *)(bus->rb.area + 2048);
-       bus->rirb.wp = bus->rirb.rp = 0;
-       memset(bus->rirb.cmds, 0, sizeof(bus->rirb.cmds));
-       snd_hdac_chip_writel(bus, RIRBLBASE, (u32)bus->rirb.addr);
-       snd_hdac_chip_writel(bus, RIRBUBASE, upper_32_bits(bus->rirb.addr));
-
-       /* set the rirb size to 256 entries (ULI requires explicitly) */
-       snd_hdac_chip_writeb(bus, RIRBSIZE, 0x02);
-       /* reset the rirb hw write pointer */
-       snd_hdac_chip_writew(bus, RIRBWP, AZX_RIRBWP_RST);
-       /* set N=1, get RIRB response interrupt for new entry */
-       snd_hdac_chip_writew(bus, RINTCNT, 1);
-       /* enable rirb dma and response irq */
-       if (bus->not_use_interrupts)
-               snd_hdac_chip_writeb(bus, RIRBCTL, AZX_RBCTL_DMA_EN);
-       else
-               snd_hdac_chip_writeb(bus, RIRBCTL, AZX_RBCTL_DMA_EN | AZX_RBCTL_IRQ_EN);
-       /* Accept unsolicited responses */
-       snd_hdac_chip_updatel(bus, GCTL, AZX_GCTL_UNSOL, AZX_GCTL_UNSOL);
-       spin_unlock_irq(&bus->reg_lock);
-}
-EXPORT_SYMBOL_GPL(snd_hdac_bus_init_cmd_io);
-
-/* wait for cmd dmas till they are stopped */
-static void hdac_wait_for_cmd_dmas(struct hdac_bus *bus)
-{
-       unsigned long timeout;
-
-       timeout = jiffies + msecs_to_jiffies(100);
-       while ((snd_hdac_chip_readb(bus, RIRBCTL) & AZX_RBCTL_DMA_EN)
-               && time_before(jiffies, timeout))
-               udelay(10);
-
-       timeout = jiffies + msecs_to_jiffies(100);
-       while ((snd_hdac_chip_readb(bus, CORBCTL) & AZX_CORBCTL_RUN)
-               && time_before(jiffies, timeout))
-               udelay(10);
-}
-
-/**
- * snd_hdac_bus_stop_cmd_io - clean up CORB/RIRB buffers
- * @bus: HD-audio core bus
- */
-void snd_hdac_bus_stop_cmd_io(struct hdac_bus *bus)
-{
-       spin_lock_irq(&bus->reg_lock);
-       /* disable ringbuffer DMAs */
-       snd_hdac_chip_writeb(bus, RIRBCTL, 0);
-       snd_hdac_chip_writeb(bus, CORBCTL, 0);
-       spin_unlock_irq(&bus->reg_lock);
-
-       hdac_wait_for_cmd_dmas(bus);
-
-       spin_lock_irq(&bus->reg_lock);
-       /* disable unsolicited responses */
-       snd_hdac_chip_updatel(bus, GCTL, AZX_GCTL_UNSOL, 0);
-       spin_unlock_irq(&bus->reg_lock);
-}
-EXPORT_SYMBOL_GPL(snd_hdac_bus_stop_cmd_io);
-
-static unsigned int azx_command_addr(u32 cmd)
-{
-       unsigned int addr = cmd >> 28;
-
-       if (snd_BUG_ON(addr >= HDA_MAX_CODECS))
-               addr = 0;
-       return addr;
-}
-
-/* receive an Immediate Response with PIO */
-static int snd_hdac_bus_wait_for_pio_response(struct hdac_bus *bus,
-                                             unsigned int addr)
-{
-       int timeout = 50;
-
-       while (timeout--) {
-               /* check IRV bit */
-               if (snd_hdac_chip_readw(bus, IRS) & AZX_IRS_VALID) {
-                       /* reuse rirb.res as the response return value */
-                       bus->rirb.res[addr] = snd_hdac_chip_readl(bus, IR);
-                       return 0;
-               }
-               udelay(1);
-       }
-
-       dev_dbg_ratelimited(bus->dev, "get_response_pio timeout: IRS=%#x\n",
-                           snd_hdac_chip_readw(bus, IRS));
-
-       bus->rirb.res[addr] = -1;
-
-       return -EIO;
-}
-
-/**
- * snd_hdac_bus_send_cmd_pio - send a command verb via Immediate Command
- * @bus: HD-audio core bus
- * @val: encoded verb value to send
- *
- * Returns zero for success or a negative error code.
- */
-static int snd_hdac_bus_send_cmd_pio(struct hdac_bus *bus, unsigned int val)
-{
-       unsigned int addr = azx_command_addr(val);
-       int timeout = 50;
-       int ret = -EIO;
-
-       spin_lock_irq(&bus->reg_lock);
-
-       while (timeout--) {
-               /* check ICB bit */
-               if (!((snd_hdac_chip_readw(bus, IRS) & AZX_IRS_BUSY))) {
-                       /* Clear IRV bit */
-                       snd_hdac_chip_updatew(bus, IRS, AZX_IRS_VALID, AZX_IRS_VALID);
-                       snd_hdac_chip_writel(bus, IC, val);
-                       /* Set ICB bit */
-                       snd_hdac_chip_updatew(bus, IRS, AZX_IRS_BUSY, AZX_IRS_BUSY);
-
-                       ret = snd_hdac_bus_wait_for_pio_response(bus, addr);
-                       goto out;
-               }
-               udelay(1);
-       }
-
-       dev_dbg_ratelimited(bus->dev, "send_cmd_pio timeout: IRS=%#x, val=%#x\n",
-                           snd_hdac_chip_readw(bus, IRS), val);
-
-out:
-       spin_unlock_irq(&bus->reg_lock);
-
-       return ret;
-}
-
-/**
- * snd_hdac_bus_get_response_pio - receive a response via Immediate Response
- * @bus: HD-audio core bus
- * @addr: codec address
- * @res: pointer to store the value, NULL when not needed
- *
- * Returns zero if a value is read, or a negative error code.
- */
-static int snd_hdac_bus_get_response_pio(struct hdac_bus *bus,
-                                        unsigned int addr, unsigned int *res)
-{
-       if (res)
-               *res = bus->rirb.res[addr];
-
-       return 0;
-}
-
-/**
- * snd_hdac_bus_send_cmd_corb - send a command verb via CORB
- * @bus: HD-audio core bus
- * @val: encoded verb value to send
- *
- * Returns zero for success or a negative error code.
- */
-static int snd_hdac_bus_send_cmd_corb(struct hdac_bus *bus, unsigned int val)
-{
-       unsigned int addr = azx_command_addr(val);
-       unsigned int wp, rp;
-
-       spin_lock_irq(&bus->reg_lock);
-
-       bus->last_cmd[azx_command_addr(val)] = val;
-
-       /* add command to corb */
-       wp = snd_hdac_chip_readw(bus, CORBWP);
-       if (wp == 0xffff) {
-               /* something wrong, controller likely turned to D3 */
-               spin_unlock_irq(&bus->reg_lock);
-               return -EIO;
-       }
-       wp++;
-       wp %= AZX_MAX_CORB_ENTRIES;
-
-       rp = snd_hdac_chip_readw(bus, CORBRP);
-       if (wp == rp) {
-               /* oops, it's full */
-               spin_unlock_irq(&bus->reg_lock);
-               return -EAGAIN;
-       }
-
-       bus->rirb.cmds[addr]++;
-       bus->corb.buf[wp] = cpu_to_le32(val);
-       snd_hdac_chip_writew(bus, CORBWP, wp);
-
-       spin_unlock_irq(&bus->reg_lock);
-
-       return 0;
-}
-
-#define AZX_RIRB_EX_UNSOL_EV   (1<<4)
-
-/**
- * snd_hdac_bus_update_rirb - retrieve RIRB entries
- * @bus: HD-audio core bus
- *
- * Usually called from interrupt handler.
- * The caller needs bus->reg_lock spinlock before calling this.
- */
-void snd_hdac_bus_update_rirb(struct hdac_bus *bus)
-{
-       unsigned int rp, wp;
-       unsigned int addr;
-       u32 res, res_ex;
-
-       wp = snd_hdac_chip_readw(bus, RIRBWP);
-       if (wp == 0xffff) {
-               /* something wrong, controller likely turned to D3 */
-               return;
-       }
-
-       if (wp == bus->rirb.wp)
-               return;
-       bus->rirb.wp = wp;
-
-       while (bus->rirb.rp != wp) {
-               bus->rirb.rp++;
-               bus->rirb.rp %= AZX_MAX_RIRB_ENTRIES;
-
-               rp = bus->rirb.rp << 1; /* an RIRB entry is 8-bytes */
-               res_ex = le32_to_cpu(bus->rirb.buf[rp + 1]);
-               res = le32_to_cpu(bus->rirb.buf[rp]);
-               addr = res_ex & 0xf;
-               if (addr >= HDA_MAX_CODECS) {
-                       dev_err(bus->dev,
-                               "spurious response %#x:%#x, rp = %d, wp = %d",
-                               res, res_ex, bus->rirb.rp, wp);
-                       snd_BUG();
-               } else if (res_ex & AZX_RIRB_EX_UNSOL_EV)
-                       snd_hdac_bus_queue_event(bus, res, res_ex);
-               else if (bus->rirb.cmds[addr]) {
-                       bus->rirb.res[addr] = res;
-                       bus->rirb.cmds[addr]--;
-                       if (!bus->rirb.cmds[addr] &&
-                           waitqueue_active(&bus->rirb_wq))
-                               wake_up(&bus->rirb_wq);
-               } else {
-                       dev_err_ratelimited(bus->dev,
-                               "spurious response %#x:%#x, last cmd=%#08x\n",
-                               res, res_ex, bus->last_cmd[addr]);
-               }
-       }
-}
-EXPORT_SYMBOL_GPL(snd_hdac_bus_update_rirb);
-
-/**
- * snd_hdac_bus_get_response_rirb - receive a response via RIRB
- * @bus: HD-audio core bus
- * @addr: codec address
- * @res: pointer to store the value, NULL when not needed
- *
- * Returns zero if a value is read, or a negative error code.
- */
-static int snd_hdac_bus_get_response_rirb(struct hdac_bus *bus,
-                                         unsigned int addr, unsigned int *res)
-{
-       unsigned long timeout;
-       unsigned long loopcounter;
-       wait_queue_entry_t wait;
-       bool warned = false;
-
-       init_wait_entry(&wait, 0);
-       timeout = jiffies + msecs_to_jiffies(1000);
-
-       for (loopcounter = 0;; loopcounter++) {
-               spin_lock_irq(&bus->reg_lock);
-               if (!bus->polling_mode)
-                       prepare_to_wait(&bus->rirb_wq, &wait,
-                                       TASK_UNINTERRUPTIBLE);
-               if (bus->polling_mode)
-                       snd_hdac_bus_update_rirb(bus);
-               if (!bus->rirb.cmds[addr]) {
-                       if (res)
-                               *res = bus->rirb.res[addr]; /* the last value */
-                       if (!bus->polling_mode)
-                               finish_wait(&bus->rirb_wq, &wait);
-                       spin_unlock_irq(&bus->reg_lock);
-                       return 0;
-               }
-               spin_unlock_irq(&bus->reg_lock);
-               if (time_after(jiffies, timeout))
-                       break;
-#define LOOP_COUNT_MAX 3000
-               if (!bus->polling_mode) {
-                       schedule_timeout(msecs_to_jiffies(2));
-               } else if (bus->needs_damn_long_delay ||
-                          loopcounter > LOOP_COUNT_MAX) {
-                       if (loopcounter > LOOP_COUNT_MAX && !warned) {
-                               dev_dbg_ratelimited(bus->dev,
-                                                   "too slow response, last cmd=%#08x\n",
-                                                   bus->last_cmd[addr]);
-                               warned = true;
-                       }
-                       msleep(2); /* temporary workaround */
-               } else {
-                       udelay(10);
-                       cond_resched();
-               }
-       }
-
-       if (!bus->polling_mode)
-               finish_wait(&bus->rirb_wq, &wait);
-
-       return -EIO;
-}
-
-/**
- * snd_hdac_bus_send_cmd - send a command verb via CORB or PIO
- * @bus: HD-audio core bus
- * @val: encoded verb value to send
- *
- * Returns zero for success or a negative error code.
- */
-int snd_hdac_bus_send_cmd(struct hdac_bus *bus, unsigned int val)
-{
-       if (bus->use_pio_for_commands)
-               return snd_hdac_bus_send_cmd_pio(bus, val);
-
-       return snd_hdac_bus_send_cmd_corb(bus, val);
-}
-EXPORT_SYMBOL_GPL(snd_hdac_bus_send_cmd);
-
-/**
- * snd_hdac_bus_get_response - receive a response via RIRB or PIO
- * @bus: HD-audio core bus
- * @addr: codec address
- * @res: pointer to store the value, NULL when not needed
- *
- * Returns zero if a value is read, or a negative error code.
- */
-int snd_hdac_bus_get_response(struct hdac_bus *bus, unsigned int addr,
-                             unsigned int *res)
-{
-       if (bus->use_pio_for_commands)
-               return snd_hdac_bus_get_response_pio(bus, addr, res);
-
-       return snd_hdac_bus_get_response_rirb(bus, addr, res);
-}
-EXPORT_SYMBOL_GPL(snd_hdac_bus_get_response);
-
-#define HDAC_MAX_CAPS 10
-/**
- * snd_hdac_bus_parse_capabilities - parse capability structure
- * @bus: the pointer to bus object
- *
- * Returns 0 if successful, or a negative error code.
- */
-int snd_hdac_bus_parse_capabilities(struct hdac_bus *bus)
-{
-       unsigned int cur_cap;
-       unsigned int offset;
-       unsigned int counter = 0;
-
-       offset = snd_hdac_chip_readw(bus, LLCH);
-
-       /* Lets walk the linked capabilities list */
-       do {
-               cur_cap = _snd_hdac_chip_readl(bus, offset);
-
-               dev_dbg(bus->dev, "Capability version: 0x%x\n",
-                       (cur_cap & AZX_CAP_HDR_VER_MASK) >> AZX_CAP_HDR_VER_OFF);
-
-               dev_dbg(bus->dev, "HDA capability ID: 0x%x\n",
-                       (cur_cap & AZX_CAP_HDR_ID_MASK) >> AZX_CAP_HDR_ID_OFF);
-
-               if (cur_cap == -1) {
-                       dev_dbg(bus->dev, "Invalid capability reg read\n");
-                       break;
-               }
-
-               switch ((cur_cap & AZX_CAP_HDR_ID_MASK) >> AZX_CAP_HDR_ID_OFF) {
-               case AZX_ML_CAP_ID:
-                       dev_dbg(bus->dev, "Found ML capability\n");
-                       bus->mlcap = bus->remap_addr + offset;
-                       break;
-
-               case AZX_GTS_CAP_ID:
-                       dev_dbg(bus->dev, "Found GTS capability offset=%x\n", offset);
-                       bus->gtscap = bus->remap_addr + offset;
-                       break;
-
-               case AZX_PP_CAP_ID:
-                       /* PP capability found, the Audio DSP is present */
-                       dev_dbg(bus->dev, "Found PP capability offset=%x\n", offset);
-                       bus->ppcap = bus->remap_addr + offset;
-                       break;
-
-               case AZX_SPB_CAP_ID:
-                       /* SPIB capability found, handler function */
-                       dev_dbg(bus->dev, "Found SPB capability\n");
-                       bus->spbcap = bus->remap_addr + offset;
-                       break;
-
-               case AZX_DRSM_CAP_ID:
-                       /* DMA resume  capability found, handler function */
-                       dev_dbg(bus->dev, "Found DRSM capability\n");
-                       bus->drsmcap = bus->remap_addr + offset;
-                       break;
-
-               default:
-                       dev_err(bus->dev, "Unknown capability %d\n", cur_cap);
-                       cur_cap = 0;
-                       break;
-               }
-
-               counter++;
-
-               if (counter > HDAC_MAX_CAPS) {
-                       dev_err(bus->dev, "We exceeded HDAC capabilities!!!\n");
-                       break;
-               }
-
-               /* read the offset of next capability */
-               offset = cur_cap & AZX_CAP_HDR_NXT_PTR_MASK;
-
-       } while (offset);
-
-       return 0;
-}
-EXPORT_SYMBOL_GPL(snd_hdac_bus_parse_capabilities);
-
-/*
- * Lowlevel interface
- */
-
-/**
- * snd_hdac_bus_enter_link_reset - enter link reset
- * @bus: HD-audio core bus
- *
- * Enter to the link reset state.
- */
-void snd_hdac_bus_enter_link_reset(struct hdac_bus *bus)
-{
-       unsigned long timeout;
-
-       /* reset controller */
-       snd_hdac_chip_updatel(bus, GCTL, AZX_GCTL_RESET, 0);
-
-       timeout = jiffies + msecs_to_jiffies(100);
-       while ((snd_hdac_chip_readb(bus, GCTL) & AZX_GCTL_RESET) &&
-              time_before(jiffies, timeout))
-               usleep_range(500, 1000);
-}
-EXPORT_SYMBOL_GPL(snd_hdac_bus_enter_link_reset);
-
-/**
- * snd_hdac_bus_exit_link_reset - exit link reset
- * @bus: HD-audio core bus
- *
- * Exit from the link reset state.
- */
-void snd_hdac_bus_exit_link_reset(struct hdac_bus *bus)
-{
-       unsigned long timeout;
-
-       snd_hdac_chip_updateb(bus, GCTL, AZX_GCTL_RESET, AZX_GCTL_RESET);
-
-       timeout = jiffies + msecs_to_jiffies(100);
-       while (!snd_hdac_chip_readb(bus, GCTL) && time_before(jiffies, timeout))
-               usleep_range(500, 1000);
-}
-EXPORT_SYMBOL_GPL(snd_hdac_bus_exit_link_reset);
-
-/* reset codec link */
-int snd_hdac_bus_reset_link(struct hdac_bus *bus, bool full_reset)
-{
-       if (!full_reset)
-               goto skip_reset;
-
-       /* clear STATESTS if not in reset */
-       if (snd_hdac_chip_readb(bus, GCTL) & AZX_GCTL_RESET)
-               snd_hdac_chip_writew(bus, STATESTS, STATESTS_INT_MASK);
-
-       /* reset controller */
-       snd_hdac_bus_enter_link_reset(bus);
-
-       /* delay for >= 100us for codec PLL to settle per spec
-        * Rev 0.9 section 5.5.1
-        */
-       usleep_range(500, 1000);
-
-       /* Bring controller out of reset */
-       snd_hdac_bus_exit_link_reset(bus);
-
-       /* Brent Chartrand said to wait >= 540us for codecs to initialize */
-       usleep_range(1000, 1200);
-
- skip_reset:
-       /* check to see if controller is ready */
-       if (!snd_hdac_chip_readb(bus, GCTL)) {
-               dev_dbg(bus->dev, "controller not ready!\n");
-               return -EBUSY;
-       }
-
-       /* detect codecs */
-       if (!bus->codec_mask) {
-               bus->codec_mask = snd_hdac_chip_readw(bus, STATESTS);
-               dev_dbg(bus->dev, "codec_mask = 0x%lx\n", bus->codec_mask);
-       }
-
-       return 0;
-}
-EXPORT_SYMBOL_GPL(snd_hdac_bus_reset_link);
-
-/* enable interrupts */
-static void azx_int_enable(struct hdac_bus *bus)
-{
-       /* enable controller CIE and GIE */
-       snd_hdac_chip_updatel(bus, INTCTL,
-                             AZX_INT_CTRL_EN | AZX_INT_GLOBAL_EN,
-                             AZX_INT_CTRL_EN | AZX_INT_GLOBAL_EN);
-}
-
-/* disable interrupts */
-static void azx_int_disable(struct hdac_bus *bus)
-{
-       struct hdac_stream *azx_dev;
-
-       /* disable interrupts in stream descriptor */
-       list_for_each_entry(azx_dev, &bus->stream_list, list)
-               snd_hdac_stream_updateb(azx_dev, SD_CTL, SD_INT_MASK, 0);
-
-       /* disable SIE for all streams & disable controller CIE and GIE */
-       snd_hdac_chip_writel(bus, INTCTL, 0);
-}
-
-/* clear interrupts */
-static void azx_int_clear(struct hdac_bus *bus)
-{
-       struct hdac_stream *azx_dev;
-
-       /* clear stream status */
-       list_for_each_entry(azx_dev, &bus->stream_list, list)
-               snd_hdac_stream_writeb(azx_dev, SD_STS, SD_INT_MASK);
-
-       /* clear STATESTS */
-       snd_hdac_chip_writew(bus, STATESTS, STATESTS_INT_MASK);
-
-       /* clear rirb status */
-       snd_hdac_chip_writeb(bus, RIRBSTS, RIRB_INT_MASK);
-
-       /* clear int status */
-       snd_hdac_chip_writel(bus, INTSTS, AZX_INT_CTRL_EN | AZX_INT_ALL_STREAM);
-}
-
-/**
- * snd_hdac_bus_init_chip - reset and start the controller registers
- * @bus: HD-audio core bus
- * @full_reset: Do full reset
- */
-bool snd_hdac_bus_init_chip(struct hdac_bus *bus, bool full_reset)
-{
-       if (bus->chip_init)
-               return false;
-
-       /* reset controller */
-       snd_hdac_bus_reset_link(bus, full_reset);
-
-       /* clear interrupts */
-       azx_int_clear(bus);
-
-       /* initialize the codec command I/O */
-       snd_hdac_bus_init_cmd_io(bus);
-
-       /* enable interrupts after CORB/RIRB buffers are initialized above */
-       azx_int_enable(bus);
-
-       /* program the position buffer */
-       if (bus->use_posbuf && bus->posbuf.addr) {
-               snd_hdac_chip_writel(bus, DPLBASE, (u32)bus->posbuf.addr);
-               snd_hdac_chip_writel(bus, DPUBASE, upper_32_bits(bus->posbuf.addr));
-       }
-
-       bus->chip_init = true;
-
-       return true;
-}
-EXPORT_SYMBOL_GPL(snd_hdac_bus_init_chip);
-
-/**
- * snd_hdac_bus_stop_chip - disable the whole IRQ and I/Os
- * @bus: HD-audio core bus
- */
-void snd_hdac_bus_stop_chip(struct hdac_bus *bus)
-{
-       if (!bus->chip_init)
-               return;
-
-       /* disable interrupts */
-       azx_int_disable(bus);
-       azx_int_clear(bus);
-
-       /* disable CORB/RIRB */
-       snd_hdac_bus_stop_cmd_io(bus);
-
-       /* disable position buffer */
-       if (bus->posbuf.addr) {
-               snd_hdac_chip_writel(bus, DPLBASE, 0);
-               snd_hdac_chip_writel(bus, DPUBASE, 0);
-       }
-
-       bus->chip_init = false;
-}
-EXPORT_SYMBOL_GPL(snd_hdac_bus_stop_chip);
-
-/**
- * snd_hdac_bus_handle_stream_irq - interrupt handler for streams
- * @bus: HD-audio core bus
- * @status: INTSTS register value
- * @ack: callback to be called for woken streams
- *
- * Returns the bits of handled streams, or zero if no stream is handled.
- */
-int snd_hdac_bus_handle_stream_irq(struct hdac_bus *bus, unsigned int status,
-                                   void (*ack)(struct hdac_bus *,
-                                               struct hdac_stream *))
-{
-       struct hdac_stream *azx_dev;
-       u8 sd_status;
-       int handled = 0;
-
-       list_for_each_entry(azx_dev, &bus->stream_list, list) {
-               if (status & azx_dev->sd_int_sta_mask) {
-                       sd_status = snd_hdac_stream_readb(azx_dev, SD_STS);
-                       snd_hdac_stream_writeb(azx_dev, SD_STS, SD_INT_MASK);
-                       handled |= 1 << azx_dev->index;
-                       if ((!azx_dev->substream && !azx_dev->cstream) ||
-                           !azx_dev->running || !(sd_status & SD_INT_COMPLETE))
-                               continue;
-                       if (ack)
-                               ack(bus, azx_dev);
-               }
-       }
-       return handled;
-}
-EXPORT_SYMBOL_GPL(snd_hdac_bus_handle_stream_irq);
-
-/**
- * snd_hdac_bus_alloc_stream_pages - allocate BDL and other buffers
- * @bus: HD-audio core bus
- *
- * Call this after assigning the all streams.
- * Returns zero for success, or a negative error code.
- */
-int snd_hdac_bus_alloc_stream_pages(struct hdac_bus *bus)
-{
-       struct hdac_stream *s;
-       int num_streams = 0;
-       int dma_type = bus->dma_type ? bus->dma_type : SNDRV_DMA_TYPE_DEV;
-       int err;
-
-       list_for_each_entry(s, &bus->stream_list, list) {
-               /* allocate memory for the BDL for each stream */
-               err = snd_dma_alloc_pages(dma_type, bus->dev,
-                                         BDL_SIZE, &s->bdl);
-               num_streams++;
-               if (err < 0)
-                       return -ENOMEM;
-       }
-
-       if (WARN_ON(!num_streams))
-               return -EINVAL;
-       /* allocate memory for the position buffer */
-       err = snd_dma_alloc_pages(dma_type, bus->dev,
-                                 num_streams * 8, &bus->posbuf);
-       if (err < 0)
-               return -ENOMEM;
-       list_for_each_entry(s, &bus->stream_list, list)
-               s->posbuf = (__le32 *)(bus->posbuf.area + s->index * 8);
-
-       /* single page (at least 4096 bytes) must suffice for both ringbuffes */
-       return snd_dma_alloc_pages(dma_type, bus->dev, PAGE_SIZE, &bus->rb);
-}
-EXPORT_SYMBOL_GPL(snd_hdac_bus_alloc_stream_pages);
-
-/**
- * snd_hdac_bus_free_stream_pages - release BDL and other buffers
- * @bus: HD-audio core bus
- */
-void snd_hdac_bus_free_stream_pages(struct hdac_bus *bus)
-{
-       struct hdac_stream *s;
-
-       list_for_each_entry(s, &bus->stream_list, list) {
-               if (s->bdl.area)
-                       snd_dma_free_pages(&s->bdl);
-       }
-
-       if (bus->rb.area)
-               snd_dma_free_pages(&bus->rb);
-       if (bus->posbuf.area)
-               snd_dma_free_pages(&bus->posbuf);
-}
-EXPORT_SYMBOL_GPL(snd_hdac_bus_free_stream_pages);
-
-/**
- * snd_hdac_bus_link_power - power up/down codec link
- * @codec: HD-audio device
- * @enable: whether to power-up the link
- */
-void snd_hdac_bus_link_power(struct hdac_device *codec, bool enable)
-{
-       if (enable)
-               set_bit(codec->addr, &codec->bus->codec_powered);
-       else
-               clear_bit(codec->addr, &codec->bus->codec_powered);
-}
-EXPORT_SYMBOL_GPL(snd_hdac_bus_link_power);
diff --git a/sound/hda/hdac_device.c b/sound/hda/hdac_device.c
deleted file mode 100644 (file)
index 018f9e1..0000000
+++ /dev/null
@@ -1,1170 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0-only
-/*
- * HD-audio codec core device
- */
-
-#include <linux/init.h>
-#include <linux/delay.h>
-#include <linux/device.h>
-#include <linux/slab.h>
-#include <linux/module.h>
-#include <linux/export.h>
-#include <linux/pm_runtime.h>
-#include <sound/hdaudio.h>
-#include <sound/hda_regmap.h>
-#include <sound/pcm.h>
-#include <sound/pcm_params.h>
-#include "local.h"
-
-static void setup_fg_nodes(struct hdac_device *codec);
-static int get_codec_vendor_name(struct hdac_device *codec);
-
-static void default_release(struct device *dev)
-{
-       snd_hdac_device_exit(dev_to_hdac_dev(dev));
-}
-
-/**
- * snd_hdac_device_init - initialize the HD-audio codec base device
- * @codec: device to initialize
- * @bus: but to attach
- * @name: device name string
- * @addr: codec address
- *
- * Returns zero for success or a negative error code.
- *
- * This function increments the runtime PM counter and marks it active.
- * The caller needs to turn it off appropriately later.
- *
- * The caller needs to set the device's release op properly by itself.
- */
-int snd_hdac_device_init(struct hdac_device *codec, struct hdac_bus *bus,
-                        const char *name, unsigned int addr)
-{
-       struct device *dev;
-       hda_nid_t fg;
-       int err;
-
-       dev = &codec->dev;
-       device_initialize(dev);
-       dev->parent = bus->dev;
-       dev->bus = &snd_hda_bus_type;
-       dev->release = default_release;
-       dev->groups = hdac_dev_attr_groups;
-       dev_set_name(dev, "%s", name);
-       device_enable_async_suspend(dev);
-
-       codec->bus = bus;
-       codec->addr = addr;
-       codec->type = HDA_DEV_CORE;
-       mutex_init(&codec->widget_lock);
-       mutex_init(&codec->regmap_lock);
-       pm_runtime_set_active(&codec->dev);
-       pm_runtime_get_noresume(&codec->dev);
-       atomic_set(&codec->in_pm, 0);
-
-       err = snd_hdac_bus_add_device(bus, codec);
-       if (err < 0)
-               goto error;
-
-       /* fill parameters */
-       codec->vendor_id = snd_hdac_read_parm(codec, AC_NODE_ROOT,
-                                             AC_PAR_VENDOR_ID);
-       if (codec->vendor_id == -1) {
-               /* read again, hopefully the access method was corrected
-                * in the last read...
-                */
-               codec->vendor_id = snd_hdac_read_parm(codec, AC_NODE_ROOT,
-                                                     AC_PAR_VENDOR_ID);
-       }
-
-       codec->subsystem_id = snd_hdac_read_parm(codec, AC_NODE_ROOT,
-                                                AC_PAR_SUBSYSTEM_ID);
-       codec->revision_id = snd_hdac_read_parm(codec, AC_NODE_ROOT,
-                                               AC_PAR_REV_ID);
-
-       setup_fg_nodes(codec);
-       if (!codec->afg && !codec->mfg) {
-               dev_err(dev, "no AFG or MFG node found\n");
-               err = -ENODEV;
-               goto error;
-       }
-
-       fg = codec->afg ? codec->afg : codec->mfg;
-
-       err = snd_hdac_refresh_widgets(codec);
-       if (err < 0)
-               goto error;
-
-       codec->power_caps = snd_hdac_read_parm(codec, fg, AC_PAR_POWER_STATE);
-       /* reread ssid if not set by parameter */
-       if (codec->subsystem_id == -1 || codec->subsystem_id == 0)
-               snd_hdac_read(codec, fg, AC_VERB_GET_SUBSYSTEM_ID, 0,
-                             &codec->subsystem_id);
-
-       err = get_codec_vendor_name(codec);
-       if (err < 0)
-               goto error;
-
-       codec->chip_name = kasprintf(GFP_KERNEL, "ID %x",
-                                    codec->vendor_id & 0xffff);
-       if (!codec->chip_name) {
-               err = -ENOMEM;
-               goto error;
-       }
-
-       return 0;
-
- error:
-       put_device(&codec->dev);
-       return err;
-}
-EXPORT_SYMBOL_GPL(snd_hdac_device_init);
-
-/**
- * snd_hdac_device_exit - clean up the HD-audio codec base device
- * @codec: device to clean up
- */
-void snd_hdac_device_exit(struct hdac_device *codec)
-{
-       pm_runtime_put_noidle(&codec->dev);
-       /* keep balance of runtime PM child_count in parent device */
-       pm_runtime_set_suspended(&codec->dev);
-       snd_hdac_bus_remove_device(codec->bus, codec);
-       kfree(codec->vendor_name);
-       kfree(codec->chip_name);
-}
-EXPORT_SYMBOL_GPL(snd_hdac_device_exit);
-
-/**
- * snd_hdac_device_register - register the hd-audio codec base device
- * @codec: the device to register
- */
-int snd_hdac_device_register(struct hdac_device *codec)
-{
-       int err;
-
-       err = device_add(&codec->dev);
-       if (err < 0)
-               return err;
-       mutex_lock(&codec->widget_lock);
-       err = hda_widget_sysfs_init(codec);
-       mutex_unlock(&codec->widget_lock);
-       if (err < 0) {
-               device_del(&codec->dev);
-               return err;
-       }
-
-       return 0;
-}
-EXPORT_SYMBOL_GPL(snd_hdac_device_register);
-
-/**
- * snd_hdac_device_unregister - unregister the hd-audio codec base device
- * @codec: the device to unregister
- */
-void snd_hdac_device_unregister(struct hdac_device *codec)
-{
-       if (device_is_registered(&codec->dev)) {
-               mutex_lock(&codec->widget_lock);
-               hda_widget_sysfs_exit(codec);
-               mutex_unlock(&codec->widget_lock);
-               device_del(&codec->dev);
-               snd_hdac_bus_remove_device(codec->bus, codec);
-       }
-}
-EXPORT_SYMBOL_GPL(snd_hdac_device_unregister);
-
-/**
- * snd_hdac_device_set_chip_name - set/update the codec name
- * @codec: the HDAC device
- * @name: name string to set
- *
- * Returns 0 if the name is set or updated, or a negative error code.
- */
-int snd_hdac_device_set_chip_name(struct hdac_device *codec, const char *name)
-{
-       char *newname;
-
-       if (!name)
-               return 0;
-       newname = kstrdup(name, GFP_KERNEL);
-       if (!newname)
-               return -ENOMEM;
-       kfree(codec->chip_name);
-       codec->chip_name = newname;
-       return 0;
-}
-EXPORT_SYMBOL_GPL(snd_hdac_device_set_chip_name);
-
-/**
- * snd_hdac_codec_modalias - give the module alias name
- * @codec: HDAC device
- * @buf: string buffer to store
- * @size: string buffer size
- *
- * Returns the size of string, like snprintf(), or a negative error code.
- */
-int snd_hdac_codec_modalias(const struct hdac_device *codec, char *buf, size_t size)
-{
-       return scnprintf(buf, size, "hdaudio:v%08Xr%08Xa%02X\n",
-                       codec->vendor_id, codec->revision_id, codec->type);
-}
-EXPORT_SYMBOL_GPL(snd_hdac_codec_modalias);
-
-/**
- * snd_hdac_make_cmd - compose a 32bit command word to be sent to the
- *     HD-audio controller
- * @codec: the codec object
- * @nid: NID to encode
- * @verb: verb to encode
- * @parm: parameter to encode
- *
- * Return an encoded command verb or -1 for error.
- */
-static unsigned int snd_hdac_make_cmd(struct hdac_device *codec, hda_nid_t nid,
-                                     unsigned int verb, unsigned int parm)
-{
-       u32 val, addr;
-
-       addr = codec->addr;
-       if ((addr & ~0xf) || (nid & ~0x7f) ||
-           (verb & ~0xfff) || (parm & ~0xffff)) {
-               dev_err(&codec->dev, "out of range cmd %x:%x:%x:%x\n",
-                       addr, nid, verb, parm);
-               return -1;
-       }
-
-       val = addr << 28;
-       val |= (u32)nid << 20;
-       val |= verb << 8;
-       val |= parm;
-       return val;
-}
-
-/**
- * snd_hdac_exec_verb - execute an encoded verb
- * @codec: the codec object
- * @cmd: encoded verb to execute
- * @flags: optional flags, pass zero for default
- * @res: the pointer to store the result, NULL if running async
- *
- * Returns zero if successful, or a negative error code.
- *
- * This calls the exec_verb op when set in hdac_codec.  If not,
- * call the default snd_hdac_bus_exec_verb().
- */
-int snd_hdac_exec_verb(struct hdac_device *codec, unsigned int cmd,
-                      unsigned int flags, unsigned int *res)
-{
-       if (codec->exec_verb)
-               return codec->exec_verb(codec, cmd, flags, res);
-       return snd_hdac_bus_exec_verb(codec->bus, codec->addr, cmd, res);
-}
-
-
-/**
- * snd_hdac_read - execute a verb
- * @codec: the codec object
- * @nid: NID to execute a verb
- * @verb: verb to execute
- * @parm: parameter for a verb
- * @res: the pointer to store the result, NULL if running async
- *
- * Returns zero if successful, or a negative error code.
- */
-int snd_hdac_read(struct hdac_device *codec, hda_nid_t nid,
-                 unsigned int verb, unsigned int parm, unsigned int *res)
-{
-       unsigned int cmd = snd_hdac_make_cmd(codec, nid, verb, parm);
-
-       return snd_hdac_exec_verb(codec, cmd, 0, res);
-}
-EXPORT_SYMBOL_GPL(snd_hdac_read);
-
-/**
- * _snd_hdac_read_parm - read a parmeter
- * @codec: the codec object
- * @nid: NID to read a parameter
- * @parm: parameter to read
- * @res: pointer to store the read value
- *
- * This function returns zero or an error unlike snd_hdac_read_parm().
- */
-int _snd_hdac_read_parm(struct hdac_device *codec, hda_nid_t nid, int parm,
-                       unsigned int *res)
-{
-       unsigned int cmd;
-
-       cmd = snd_hdac_regmap_encode_verb(nid, AC_VERB_PARAMETERS) | parm;
-       return snd_hdac_regmap_read_raw(codec, cmd, res);
-}
-EXPORT_SYMBOL_GPL(_snd_hdac_read_parm);
-
-/**
- * snd_hdac_read_parm_uncached - read a codec parameter without caching
- * @codec: the codec object
- * @nid: NID to read a parameter
- * @parm: parameter to read
- *
- * Returns -1 for error.  If you need to distinguish the error more
- * strictly, use snd_hdac_read() directly.
- */
-int snd_hdac_read_parm_uncached(struct hdac_device *codec, hda_nid_t nid,
-                               int parm)
-{
-       unsigned int cmd, val;
-
-       cmd = snd_hdac_regmap_encode_verb(nid, AC_VERB_PARAMETERS) | parm;
-       if (snd_hdac_regmap_read_raw_uncached(codec, cmd, &val) < 0)
-               return -1;
-       return val;
-}
-EXPORT_SYMBOL_GPL(snd_hdac_read_parm_uncached);
-
-/**
- * snd_hdac_override_parm - override read-only parameters
- * @codec: the codec object
- * @nid: NID for the parameter
- * @parm: the parameter to change
- * @val: the parameter value to overwrite
- */
-int snd_hdac_override_parm(struct hdac_device *codec, hda_nid_t nid,
-                          unsigned int parm, unsigned int val)
-{
-       unsigned int verb = (AC_VERB_PARAMETERS << 8) | (nid << 20) | parm;
-       int err;
-
-       if (!codec->regmap)
-               return -EINVAL;
-
-       codec->caps_overwriting = true;
-       err = snd_hdac_regmap_write_raw(codec, verb, val);
-       codec->caps_overwriting = false;
-       return err;
-}
-EXPORT_SYMBOL_GPL(snd_hdac_override_parm);
-
-/**
- * snd_hdac_get_sub_nodes - get start NID and number of subtree nodes
- * @codec: the codec object
- * @nid: NID to inspect
- * @start_id: the pointer to store the starting NID
- *
- * Returns the number of subtree nodes or zero if not found.
- * This function reads parameters always without caching.
- */
-int snd_hdac_get_sub_nodes(struct hdac_device *codec, hda_nid_t nid,
-                          hda_nid_t *start_id)
-{
-       unsigned int parm;
-
-       parm = snd_hdac_read_parm_uncached(codec, nid, AC_PAR_NODE_COUNT);
-       if (parm == -1) {
-               *start_id = 0;
-               return 0;
-       }
-       *start_id = (parm >> 16) & 0x7fff;
-       return (int)(parm & 0x7fff);
-}
-EXPORT_SYMBOL_GPL(snd_hdac_get_sub_nodes);
-
-/*
- * look for an AFG and MFG nodes
- */
-static void setup_fg_nodes(struct hdac_device *codec)
-{
-       int i, total_nodes, function_id;
-       hda_nid_t nid;
-
-       total_nodes = snd_hdac_get_sub_nodes(codec, AC_NODE_ROOT, &nid);
-       for (i = 0; i < total_nodes; i++, nid++) {
-               function_id = snd_hdac_read_parm(codec, nid,
-                                                AC_PAR_FUNCTION_TYPE);
-               switch (function_id & 0xff) {
-               case AC_GRP_AUDIO_FUNCTION:
-                       codec->afg = nid;
-                       codec->afg_function_id = function_id & 0xff;
-                       codec->afg_unsol = (function_id >> 8) & 1;
-                       break;
-               case AC_GRP_MODEM_FUNCTION:
-                       codec->mfg = nid;
-                       codec->mfg_function_id = function_id & 0xff;
-                       codec->mfg_unsol = (function_id >> 8) & 1;
-                       break;
-               default:
-                       break;
-               }
-       }
-}
-
-/**
- * snd_hdac_refresh_widgets - Reset the widget start/end nodes
- * @codec: the codec object
- */
-int snd_hdac_refresh_widgets(struct hdac_device *codec)
-{
-       hda_nid_t start_nid;
-       int nums, err = 0;
-
-       /*
-        * Serialize against multiple threads trying to update the sysfs
-        * widgets array.
-        */
-       mutex_lock(&codec->widget_lock);
-       nums = snd_hdac_get_sub_nodes(codec, codec->afg, &start_nid);
-       if (!start_nid || nums <= 0 || nums >= 0xff) {
-               dev_err(&codec->dev, "cannot read sub nodes for FG 0x%02x\n",
-                       codec->afg);
-               err = -EINVAL;
-               goto unlock;
-       }
-
-       err = hda_widget_sysfs_reinit(codec, start_nid, nums);
-       if (err < 0)
-               goto unlock;
-
-       codec->num_nodes = nums;
-       codec->start_nid = start_nid;
-       codec->end_nid = start_nid + nums;
-unlock:
-       mutex_unlock(&codec->widget_lock);
-       return err;
-}
-EXPORT_SYMBOL_GPL(snd_hdac_refresh_widgets);
-
-/* return CONNLIST_LEN parameter of the given widget */
-static unsigned int get_num_conns(struct hdac_device *codec, hda_nid_t nid)
-{
-       unsigned int wcaps = snd_hdac_get_wcaps(codec, nid);
-       unsigned int parm;
-
-       if (!(wcaps & AC_WCAP_CONN_LIST) &&
-           snd_hdac_get_wcaps_type(wcaps) != AC_WID_VOL_KNB)
-               return 0;
-
-       parm = snd_hdac_read_parm(codec, nid, AC_PAR_CONNLIST_LEN);
-       if (parm == -1)
-               parm = 0;
-       return parm;
-}
-
-/**
- * snd_hdac_get_connections - get a widget connection list
- * @codec: the codec object
- * @nid: NID
- * @conn_list: the array to store the results, can be NULL
- * @max_conns: the max size of the given array
- *
- * Returns the number of connected widgets, zero for no connection, or a
- * negative error code.  When the number of elements don't fit with the
- * given array size, it returns -ENOSPC.
- *
- * When @conn_list is NULL, it just checks the number of connections.
- */
-int snd_hdac_get_connections(struct hdac_device *codec, hda_nid_t nid,
-                            hda_nid_t *conn_list, int max_conns)
-{
-       unsigned int parm;
-       int i, conn_len, conns, err;
-       unsigned int shift, num_elems, mask;
-       hda_nid_t prev_nid;
-       int null_count = 0;
-
-       parm = get_num_conns(codec, nid);
-       if (!parm)
-               return 0;
-
-       if (parm & AC_CLIST_LONG) {
-               /* long form */
-               shift = 16;
-               num_elems = 2;
-       } else {
-               /* short form */
-               shift = 8;
-               num_elems = 4;
-       }
-       conn_len = parm & AC_CLIST_LENGTH;
-       mask = (1 << (shift-1)) - 1;
-
-       if (!conn_len)
-               return 0; /* no connection */
-
-       if (conn_len == 1) {
-               /* single connection */
-               err = snd_hdac_read(codec, nid, AC_VERB_GET_CONNECT_LIST, 0,
-                                   &parm);
-               if (err < 0)
-                       return err;
-               if (conn_list)
-                       conn_list[0] = parm & mask;
-               return 1;
-       }
-
-       /* multi connection */
-       conns = 0;
-       prev_nid = 0;
-       for (i = 0; i < conn_len; i++) {
-               int range_val;
-               hda_nid_t val, n;
-
-               if (i % num_elems == 0) {
-                       err = snd_hdac_read(codec, nid,
-                                           AC_VERB_GET_CONNECT_LIST, i,
-                                           &parm);
-                       if (err < 0)
-                               return -EIO;
-               }
-               range_val = !!(parm & (1 << (shift-1))); /* ranges */
-               val = parm & mask;
-               if (val == 0 && null_count++) {  /* no second chance */
-                       dev_dbg(&codec->dev,
-                               "invalid CONNECT_LIST verb %x[%i]:%x\n",
-                               nid, i, parm);
-                       return 0;
-               }
-               parm >>= shift;
-               if (range_val) {
-                       /* ranges between the previous and this one */
-                       if (!prev_nid || prev_nid >= val) {
-                               dev_warn(&codec->dev,
-                                        "invalid dep_range_val %x:%x\n",
-                                        prev_nid, val);
-                               continue;
-                       }
-                       for (n = prev_nid + 1; n <= val; n++) {
-                               if (conn_list) {
-                                       if (conns >= max_conns)
-                                               return -ENOSPC;
-                                       conn_list[conns] = n;
-                               }
-                               conns++;
-                       }
-               } else {
-                       if (conn_list) {
-                               if (conns >= max_conns)
-                                       return -ENOSPC;
-                               conn_list[conns] = val;
-                       }
-                       conns++;
-               }
-               prev_nid = val;
-       }
-       return conns;
-}
-EXPORT_SYMBOL_GPL(snd_hdac_get_connections);
-
-#ifdef CONFIG_PM
-/**
- * snd_hdac_power_up - power up the codec
- * @codec: the codec object
- *
- * This function calls the runtime PM helper to power up the given codec.
- * Unlike snd_hdac_power_up_pm(), you should call this only for the code
- * path that isn't included in PM path.  Otherwise it gets stuck.
- *
- * Returns zero if successful, or a negative error code.
- */
-int snd_hdac_power_up(struct hdac_device *codec)
-{
-       return pm_runtime_get_sync(&codec->dev);
-}
-EXPORT_SYMBOL_GPL(snd_hdac_power_up);
-
-/**
- * snd_hdac_power_down - power down the codec
- * @codec: the codec object
- *
- * Returns zero if successful, or a negative error code.
- */
-int snd_hdac_power_down(struct hdac_device *codec)
-{
-       struct device *dev = &codec->dev;
-
-       return pm_runtime_put_autosuspend(dev);
-}
-EXPORT_SYMBOL_GPL(snd_hdac_power_down);
-
-/**
- * snd_hdac_power_up_pm - power up the codec
- * @codec: the codec object
- *
- * This function can be called in a recursive code path like init code
- * which may be called by PM suspend/resume again.  OTOH, if a power-up
- * call must wake up the sleeper (e.g. in a kctl callback), use
- * snd_hdac_power_up() instead.
- *
- * Returns zero if successful, or a negative error code.
- */
-int snd_hdac_power_up_pm(struct hdac_device *codec)
-{
-       if (!atomic_inc_not_zero(&codec->in_pm))
-               return snd_hdac_power_up(codec);
-       return 0;
-}
-EXPORT_SYMBOL_GPL(snd_hdac_power_up_pm);
-
-/* like snd_hdac_power_up_pm(), but only increment the pm count when
- * already powered up.  Returns -1 if not powered up, 1 if incremented
- * or 0 if unchanged.  Only used in hdac_regmap.c
- */
-int snd_hdac_keep_power_up(struct hdac_device *codec)
-{
-       if (!atomic_inc_not_zero(&codec->in_pm)) {
-               int ret = pm_runtime_get_if_active(&codec->dev);
-               if (!ret)
-                       return -1;
-               if (ret < 0)
-                       return 0;
-       }
-       return 1;
-}
-
-/**
- * snd_hdac_power_down_pm - power down the codec
- * @codec: the codec object
- *
- * Like snd_hdac_power_up_pm(), this function is used in a recursive
- * code path like init code which may be called by PM suspend/resume again.
- *
- * Returns zero if successful, or a negative error code.
- */
-int snd_hdac_power_down_pm(struct hdac_device *codec)
-{
-       if (atomic_dec_if_positive(&codec->in_pm) < 0)
-               return snd_hdac_power_down(codec);
-       return 0;
-}
-EXPORT_SYMBOL_GPL(snd_hdac_power_down_pm);
-#endif
-
-/* codec vendor labels */
-struct hda_vendor_id {
-       unsigned int id;
-       const char *name;
-};
-
-static const struct hda_vendor_id hda_vendor_ids[] = {
-       { 0x0014, "Loongson" },
-       { 0x1002, "ATI" },
-       { 0x1013, "Cirrus Logic" },
-       { 0x1057, "Motorola" },
-       { 0x1095, "Silicon Image" },
-       { 0x10de, "Nvidia" },
-       { 0x10ec, "Realtek" },
-       { 0x1102, "Creative" },
-       { 0x1106, "VIA" },
-       { 0x111d, "IDT" },
-       { 0x11c1, "LSI" },
-       { 0x11d4, "Analog Devices" },
-       { 0x13f6, "C-Media" },
-       { 0x14f1, "Conexant" },
-       { 0x17e8, "Chrontel" },
-       { 0x1854, "LG" },
-       { 0x19e5, "Huawei" },
-       { 0x1aec, "Wolfson Microelectronics" },
-       { 0x1af4, "QEMU" },
-       { 0x1fa8, "Senarytech" },
-       { 0x434d, "C-Media" },
-       { 0x8086, "Intel" },
-       { 0x8384, "SigmaTel" },
-       {} /* terminator */
-};
-
-/* store the codec vendor name */
-static int get_codec_vendor_name(struct hdac_device *codec)
-{
-       const struct hda_vendor_id *c;
-       u16 vendor_id = codec->vendor_id >> 16;
-
-       for (c = hda_vendor_ids; c->id; c++) {
-               if (c->id == vendor_id) {
-                       codec->vendor_name = kstrdup(c->name, GFP_KERNEL);
-                       return codec->vendor_name ? 0 : -ENOMEM;
-               }
-       }
-
-       codec->vendor_name = kasprintf(GFP_KERNEL, "Generic %04x", vendor_id);
-       return codec->vendor_name ? 0 : -ENOMEM;
-}
-
-/*
- * stream formats
- */
-struct hda_rate_tbl {
-       unsigned int hz;
-       unsigned int alsa_bits;
-       unsigned int hda_fmt;
-};
-
-/* rate = base * mult / div */
-#define HDA_RATE(base, mult, div) \
-       (AC_FMT_BASE_##base##K | (((mult) - 1) << AC_FMT_MULT_SHIFT) | \
-        (((div) - 1) << AC_FMT_DIV_SHIFT))
-
-static const struct hda_rate_tbl rate_bits[] = {
-       /* rate in Hz, ALSA rate bitmask, HDA format value */
-
-       /* autodetected value used in snd_hda_query_supported_pcm */
-       { 8000, SNDRV_PCM_RATE_8000, HDA_RATE(48, 1, 6) },
-       { 11025, SNDRV_PCM_RATE_11025, HDA_RATE(44, 1, 4) },
-       { 16000, SNDRV_PCM_RATE_16000, HDA_RATE(48, 1, 3) },
-       { 22050, SNDRV_PCM_RATE_22050, HDA_RATE(44, 1, 2) },
-       { 32000, SNDRV_PCM_RATE_32000, HDA_RATE(48, 2, 3) },
-       { 44100, SNDRV_PCM_RATE_44100, HDA_RATE(44, 1, 1) },
-       { 48000, SNDRV_PCM_RATE_48000, HDA_RATE(48, 1, 1) },
-       { 88200, SNDRV_PCM_RATE_88200, HDA_RATE(44, 2, 1) },
-       { 96000, SNDRV_PCM_RATE_96000, HDA_RATE(48, 2, 1) },
-       { 176400, SNDRV_PCM_RATE_176400, HDA_RATE(44, 4, 1) },
-       { 192000, SNDRV_PCM_RATE_192000, HDA_RATE(48, 4, 1) },
-#define AC_PAR_PCM_RATE_BITS   11
-       /* up to bits 10, 384kHZ isn't supported properly */
-
-       /* not autodetected value */
-       { 9600, SNDRV_PCM_RATE_KNOT, HDA_RATE(48, 1, 5) },
-
-       { 0 } /* terminator */
-};
-
-static snd_pcm_format_t snd_hdac_format_normalize(snd_pcm_format_t format)
-{
-       switch (format) {
-       case SNDRV_PCM_FORMAT_S20_LE:
-       case SNDRV_PCM_FORMAT_S24_LE:
-               return SNDRV_PCM_FORMAT_S32_LE;
-
-       case SNDRV_PCM_FORMAT_U20_LE:
-       case SNDRV_PCM_FORMAT_U24_LE:
-               return SNDRV_PCM_FORMAT_U32_LE;
-
-       case SNDRV_PCM_FORMAT_S20_BE:
-       case SNDRV_PCM_FORMAT_S24_BE:
-               return SNDRV_PCM_FORMAT_S32_BE;
-
-       case SNDRV_PCM_FORMAT_U20_BE:
-       case SNDRV_PCM_FORMAT_U24_BE:
-               return SNDRV_PCM_FORMAT_U32_BE;
-
-       default:
-               return format;
-       }
-}
-
-/**
- * snd_hdac_stream_format_bits - obtain bits per sample value.
- * @format:    the PCM format.
- * @subformat: the PCM subformat.
- * @maxbits:   the maximum bits per sample.
- *
- * Return: The number of bits per sample.
- */
-unsigned int snd_hdac_stream_format_bits(snd_pcm_format_t format, snd_pcm_subformat_t subformat,
-                                        unsigned int maxbits)
-{
-       struct snd_pcm_hw_params params;
-       unsigned int bits;
-
-       memset(&params, 0, sizeof(params));
-
-       params_set_format(&params, snd_hdac_format_normalize(format));
-       snd_mask_set(hw_param_mask(&params, SNDRV_PCM_HW_PARAM_SUBFORMAT),
-                    (__force unsigned int)subformat);
-
-       bits = snd_pcm_hw_params_bits(&params);
-       if (maxbits)
-               return min(bits, maxbits);
-       return bits;
-}
-EXPORT_SYMBOL_GPL(snd_hdac_stream_format_bits);
-
-/**
- * snd_hdac_stream_format - convert format parameters to SDxFMT value.
- * @channels:  the number of channels.
- * @bits:      bits per sample.
- * @rate:      the sample rate.
- *
- * Return: The format bitset or zero if invalid.
- */
-unsigned int snd_hdac_stream_format(unsigned int channels, unsigned int bits, unsigned int rate)
-{
-       unsigned int val = 0;
-       int i;
-
-       for (i = 0; rate_bits[i].hz; i++) {
-               if (rate_bits[i].hz == rate) {
-                       val = rate_bits[i].hda_fmt;
-                       break;
-               }
-       }
-
-       if (!rate_bits[i].hz)
-               return 0;
-
-       if (channels == 0 || channels > 16)
-               return 0;
-       val |= channels - 1;
-
-       switch (bits) {
-       case 8:
-               val |= AC_FMT_BITS_8;
-               break;
-       case 16:
-               val |= AC_FMT_BITS_16;
-               break;
-       case 20:
-               val |= AC_FMT_BITS_20;
-               break;
-       case 24:
-               val |= AC_FMT_BITS_24;
-               break;
-       case 32:
-               val |= AC_FMT_BITS_32;
-               break;
-       default:
-               return 0;
-       }
-
-       return val;
-}
-EXPORT_SYMBOL_GPL(snd_hdac_stream_format);
-
-/**
- * snd_hdac_spdif_stream_format - convert format parameters to SDxFMT value.
- * @channels:  the number of channels.
- * @bits:      bits per sample.
- * @rate:      the sample rate.
- * @spdif_ctls:        HD-audio SPDIF status bits (0 if irrelevant).
- *
- * Return: The format bitset or zero if invalid.
- */
-unsigned int snd_hdac_spdif_stream_format(unsigned int channels, unsigned int bits,
-                                         unsigned int rate, unsigned short spdif_ctls)
-{
-       unsigned int val = snd_hdac_stream_format(channels, bits, rate);
-
-       if (val && spdif_ctls & AC_DIG1_NONAUDIO)
-               val |= AC_FMT_TYPE_NON_PCM;
-
-       return val;
-}
-EXPORT_SYMBOL_GPL(snd_hdac_spdif_stream_format);
-
-static unsigned int query_pcm_param(struct hdac_device *codec, hda_nid_t nid)
-{
-       unsigned int val = 0;
-
-       if (nid != codec->afg &&
-           (snd_hdac_get_wcaps(codec, nid) & AC_WCAP_FORMAT_OVRD))
-               val = snd_hdac_read_parm(codec, nid, AC_PAR_PCM);
-       if (!val || val == -1)
-               val = snd_hdac_read_parm(codec, codec->afg, AC_PAR_PCM);
-       if (!val || val == -1)
-               return 0;
-       return val;
-}
-
-static unsigned int query_stream_param(struct hdac_device *codec, hda_nid_t nid)
-{
-       unsigned int streams = snd_hdac_read_parm(codec, nid, AC_PAR_STREAM);
-
-       if (!streams || streams == -1)
-               streams = snd_hdac_read_parm(codec, codec->afg, AC_PAR_STREAM);
-       if (!streams || streams == -1)
-               return 0;
-       return streams;
-}
-
-/**
- * snd_hdac_query_supported_pcm - query the supported PCM rates and formats
- * @codec: the codec object
- * @nid: NID to query
- * @ratesp: the pointer to store the detected rate bitflags
- * @formatsp: the pointer to store the detected formats
- * @subformatsp: the pointer to store the detected subformats for S32_LE format
- * @bpsp: the pointer to store the detected format widths
- *
- * Queries the supported PCM rates and formats.  The NULL @ratesp, @formatsp,
- * @subformatsp or @bpsp argument is ignored.
- *
- * Returns 0 if successful, otherwise a negative error code.
- */
-int snd_hdac_query_supported_pcm(struct hdac_device *codec, hda_nid_t nid,
-                                u32 *ratesp, u64 *formatsp, u32 *subformatsp,
-                                unsigned int *bpsp)
-{
-       unsigned int i, val, wcaps;
-
-       wcaps = snd_hdac_get_wcaps(codec, nid);
-       val = query_pcm_param(codec, nid);
-
-       if (ratesp) {
-               u32 rates = 0;
-               for (i = 0; i < AC_PAR_PCM_RATE_BITS; i++) {
-                       if (val & (1 << i))
-                               rates |= rate_bits[i].alsa_bits;
-               }
-               if (rates == 0) {
-                       dev_err(&codec->dev,
-                               "rates == 0 (nid=0x%x, val=0x%x, ovrd=%i)\n",
-                               nid, val,
-                               (wcaps & AC_WCAP_FORMAT_OVRD) ? 1 : 0);
-                       return -EIO;
-               }
-               *ratesp = rates;
-       }
-
-       if (formatsp || subformatsp || bpsp) {
-               unsigned int streams, bps;
-               u32 subformats = 0;
-               u64 formats = 0;
-
-               streams = query_stream_param(codec, nid);
-               if (!streams)
-                       return -EIO;
-
-               bps = 0;
-               if (streams & AC_SUPFMT_PCM) {
-                       if (val & AC_SUPPCM_BITS_8) {
-                               formats |= SNDRV_PCM_FMTBIT_U8;
-                               bps = 8;
-                       }
-                       if (val & AC_SUPPCM_BITS_16) {
-                               formats |= SNDRV_PCM_FMTBIT_S16_LE;
-                               bps = 16;
-                       }
-                       if (val & AC_SUPPCM_BITS_20) {
-                               formats |= SNDRV_PCM_FMTBIT_S32_LE;
-                               subformats |= SNDRV_PCM_SUBFMTBIT_MSBITS_20;
-                               bps = 20;
-                       }
-                       if (val & AC_SUPPCM_BITS_24) {
-                               formats |= SNDRV_PCM_FMTBIT_S32_LE;
-                               subformats |= SNDRV_PCM_SUBFMTBIT_MSBITS_24;
-                               bps = 24;
-                       }
-                       if (val & AC_SUPPCM_BITS_32) {
-                               if (wcaps & AC_WCAP_DIGITAL) {
-                                       formats |= SNDRV_PCM_FMTBIT_IEC958_SUBFRAME_LE;
-                               } else {
-                                       formats |= SNDRV_PCM_FMTBIT_S32_LE;
-                                       subformats |= SNDRV_PCM_SUBFMTBIT_MSBITS_MAX;
-                                       bps = 32;
-                               }
-                       }
-               }
-#if 0 /* FIXME: CS4206 doesn't work, which is the only codec supporting float */
-               if (streams & AC_SUPFMT_FLOAT32) {
-                       formats |= SNDRV_PCM_FMTBIT_FLOAT_LE;
-                       if (!bps)
-                               bps = 32;
-               }
-#endif
-               if (streams == AC_SUPFMT_AC3) {
-                       /* should be exclusive */
-                       /* temporary hack: we have still no proper support
-                        * for the direct AC3 stream...
-                        */
-                       formats |= SNDRV_PCM_FMTBIT_U8;
-                       bps = 8;
-               }
-               if (formats == 0) {
-                       dev_err(&codec->dev,
-                               "formats == 0 (nid=0x%x, val=0x%x, ovrd=%i, streams=0x%x)\n",
-                               nid, val,
-                               (wcaps & AC_WCAP_FORMAT_OVRD) ? 1 : 0,
-                               streams);
-                       return -EIO;
-               }
-               if (formatsp)
-                       *formatsp = formats;
-               if (subformatsp)
-                       *subformatsp = subformats;
-               if (bpsp)
-                       *bpsp = bps;
-       }
-
-       return 0;
-}
-EXPORT_SYMBOL_GPL(snd_hdac_query_supported_pcm);
-
-/**
- * snd_hdac_is_supported_format - Check the validity of the format
- * @codec: the codec object
- * @nid: NID to check
- * @format: the HD-audio format value to check
- *
- * Check whether the given node supports the format value.
- *
- * Returns true if supported, false if not.
- */
-bool snd_hdac_is_supported_format(struct hdac_device *codec, hda_nid_t nid,
-                                 unsigned int format)
-{
-       int i;
-       unsigned int val = 0, rate, stream;
-
-       val = query_pcm_param(codec, nid);
-       if (!val)
-               return false;
-
-       rate = format & 0xff00;
-       for (i = 0; i < AC_PAR_PCM_RATE_BITS; i++)
-               if (rate_bits[i].hda_fmt == rate) {
-                       if (val & (1 << i))
-                               break;
-                       return false;
-               }
-       if (i >= AC_PAR_PCM_RATE_BITS)
-               return false;
-
-       stream = query_stream_param(codec, nid);
-       if (!stream)
-               return false;
-
-       if (stream & AC_SUPFMT_PCM) {
-               switch (format & 0xf0) {
-               case 0x00:
-                       if (!(val & AC_SUPPCM_BITS_8))
-                               return false;
-                       break;
-               case 0x10:
-                       if (!(val & AC_SUPPCM_BITS_16))
-                               return false;
-                       break;
-               case 0x20:
-                       if (!(val & AC_SUPPCM_BITS_20))
-                               return false;
-                       break;
-               case 0x30:
-                       if (!(val & AC_SUPPCM_BITS_24))
-                               return false;
-                       break;
-               case 0x40:
-                       if (!(val & AC_SUPPCM_BITS_32))
-                               return false;
-                       break;
-               default:
-                       return false;
-               }
-       } else {
-               /* FIXME: check for float32 and AC3? */
-       }
-
-       return true;
-}
-EXPORT_SYMBOL_GPL(snd_hdac_is_supported_format);
-
-static unsigned int codec_read(struct hdac_device *hdac, hda_nid_t nid,
-                       int flags, unsigned int verb, unsigned int parm)
-{
-       unsigned int cmd = snd_hdac_make_cmd(hdac, nid, verb, parm);
-       unsigned int res;
-
-       if (snd_hdac_exec_verb(hdac, cmd, flags, &res))
-               return -1;
-
-       return res;
-}
-
-static int codec_write(struct hdac_device *hdac, hda_nid_t nid,
-                       int flags, unsigned int verb, unsigned int parm)
-{
-       unsigned int cmd = snd_hdac_make_cmd(hdac, nid, verb, parm);
-
-       return snd_hdac_exec_verb(hdac, cmd, flags, NULL);
-}
-
-/**
- * snd_hdac_codec_read - send a command and get the response
- * @hdac: the HDAC device
- * @nid: NID to send the command
- * @flags: optional bit flags
- * @verb: the verb to send
- * @parm: the parameter for the verb
- *
- * Send a single command and read the corresponding response.
- *
- * Returns the obtained response value, or -1 for an error.
- */
-int snd_hdac_codec_read(struct hdac_device *hdac, hda_nid_t nid,
-                       int flags, unsigned int verb, unsigned int parm)
-{
-       return codec_read(hdac, nid, flags, verb, parm);
-}
-EXPORT_SYMBOL_GPL(snd_hdac_codec_read);
-
-/**
- * snd_hdac_codec_write - send a single command without waiting for response
- * @hdac: the HDAC device
- * @nid: NID to send the command
- * @flags: optional bit flags
- * @verb: the verb to send
- * @parm: the parameter for the verb
- *
- * Send a single command without waiting for response.
- *
- * Returns 0 if successful, or a negative error code.
- */
-int snd_hdac_codec_write(struct hdac_device *hdac, hda_nid_t nid,
-                       int flags, unsigned int verb, unsigned int parm)
-{
-       return codec_write(hdac, nid, flags, verb, parm);
-}
-EXPORT_SYMBOL_GPL(snd_hdac_codec_write);
-
-/**
- * snd_hdac_check_power_state - check whether the actual power state matches
- * with the target state
- *
- * @hdac: the HDAC device
- * @nid: NID to send the command
- * @target_state: target state to check for
- *
- * Return true if state matches, false if not
- */
-bool snd_hdac_check_power_state(struct hdac_device *hdac,
-               hda_nid_t nid, unsigned int target_state)
-{
-       unsigned int state = codec_read(hdac, nid, 0,
-                               AC_VERB_GET_POWER_STATE, 0);
-
-       if (state & AC_PWRST_ERROR)
-               return true;
-       state = (state >> 4) & 0x0f;
-       return (state == target_state);
-}
-EXPORT_SYMBOL_GPL(snd_hdac_check_power_state);
-/**
- * snd_hdac_sync_power_state - wait until actual power state matches
- * with the target state
- *
- * @codec: the HDAC device
- * @nid: NID to send the command
- * @power_state: target power state to wait for
- *
- * Return power state or PS_ERROR if codec rejects GET verb.
- */
-unsigned int snd_hdac_sync_power_state(struct hdac_device *codec,
-                       hda_nid_t nid, unsigned int power_state)
-{
-       unsigned long end_time = jiffies + msecs_to_jiffies(500);
-       unsigned int state, actual_state, count;
-
-       for (count = 0; count < 500; count++) {
-               state = snd_hdac_codec_read(codec, nid, 0,
-                               AC_VERB_GET_POWER_STATE, 0);
-               if (state & AC_PWRST_ERROR) {
-                       msleep(20);
-                       break;
-               }
-               actual_state = (state >> 4) & 0x0f;
-               if (actual_state == power_state)
-                       break;
-               if (time_after_eq(jiffies, end_time))
-                       break;
-               /* wait until the codec reachs to the target state */
-               msleep(1);
-       }
-       return state;
-}
-EXPORT_SYMBOL_GPL(snd_hdac_sync_power_state);
diff --git a/sound/hda/hdac_i915.c b/sound/hda/hdac_i915.c
deleted file mode 100644 (file)
index e942521..0000000
+++ /dev/null
@@ -1,208 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0-or-later
-/*
- *  hdac_i915.c - routines for sync between HD-A core and i915 display driver
- */
-
-#include <linux/init.h>
-#include <linux/module.h>
-#include <linux/pci.h>
-#include <sound/core.h>
-#include <sound/hdaudio.h>
-#include <sound/hda_i915.h>
-#include <sound/hda_register.h>
-#include <video/nomodeset.h>
-
-static int gpu_bind = -1;
-module_param(gpu_bind, int, 0644);
-MODULE_PARM_DESC(gpu_bind, "Whether to bind sound component to GPU "
-                          "(1=always, 0=never, -1=on nomodeset(default))");
-
-/**
- * snd_hdac_i915_set_bclk - Reprogram BCLK for HSW/BDW
- * @bus: HDA core bus
- *
- * Intel HSW/BDW display HDA controller is in GPU. Both its power and link BCLK
- * depends on GPU. Two Extended Mode registers EM4 (M value) and EM5 (N Value)
- * are used to convert CDClk (Core Display Clock) to 24MHz BCLK:
- * BCLK = CDCLK * M / N
- * The values will be lost when the display power well is disabled and need to
- * be restored to avoid abnormal playback speed.
- *
- * Call this function at initializing and changing power well, as well as
- * at ELD notifier for the hotplug.
- */
-void snd_hdac_i915_set_bclk(struct hdac_bus *bus)
-{
-       struct drm_audio_component *acomp = bus->audio_component;
-       struct pci_dev *pci = to_pci_dev(bus->dev);
-       int cdclk_freq;
-       unsigned int bclk_m, bclk_n;
-
-       if (!acomp || !acomp->ops || !acomp->ops->get_cdclk_freq)
-               return; /* only for i915 binding */
-       if (!HDA_CONTROLLER_IS_HSW(pci))
-               return; /* only HSW/BDW */
-
-       cdclk_freq = acomp->ops->get_cdclk_freq(acomp->dev);
-       switch (cdclk_freq) {
-       case 337500:
-               bclk_m = 16;
-               bclk_n = 225;
-               break;
-
-       case 450000:
-       default: /* default CDCLK 450MHz */
-               bclk_m = 4;
-               bclk_n = 75;
-               break;
-
-       case 540000:
-               bclk_m = 4;
-               bclk_n = 90;
-               break;
-
-       case 675000:
-               bclk_m = 8;
-               bclk_n = 225;
-               break;
-       }
-
-       snd_hdac_chip_writew(bus, HSW_EM4, bclk_m);
-       snd_hdac_chip_writew(bus, HSW_EM5, bclk_n);
-}
-EXPORT_SYMBOL_GPL(snd_hdac_i915_set_bclk);
-
-/* returns true if the devices can be connected for audio */
-static bool connectivity_check(struct pci_dev *i915, struct pci_dev *hdac)
-{
-       struct pci_bus *bus_a = i915->bus, *bus_b = hdac->bus;
-
-       /* directly connected on the same bus */
-       if (bus_a == bus_b)
-               return true;
-
-       bus_a = bus_a->parent;
-       bus_b = bus_b->parent;
-
-       /* connected via parent bus (may be NULL!) */
-       if (bus_a == bus_b)
-               return true;
-
-       if (!bus_a || !bus_b)
-               return false;
-
-       /*
-        * on i915 discrete GPUs with embedded HDA audio, the two
-        * devices are connected via 2nd level PCI bridge
-        */
-       bus_a = bus_a->parent;
-       bus_b = bus_b->parent;
-       if (bus_a && bus_a == bus_b)
-               return true;
-
-       return false;
-}
-
-static int i915_component_master_match(struct device *dev, int subcomponent,
-                                      void *data)
-{
-       struct pci_dev *hdac_pci, *i915_pci;
-       struct hdac_bus *bus = data;
-
-       if (!dev_is_pci(dev))
-               return 0;
-
-       hdac_pci = to_pci_dev(bus->dev);
-       i915_pci = to_pci_dev(dev);
-
-       if ((!strcmp(dev->driver->name, "i915") ||
-                !strcmp(dev->driver->name, "xe")) &&
-           subcomponent == I915_COMPONENT_AUDIO &&
-           connectivity_check(i915_pci, hdac_pci))
-               return 1;
-
-       return 0;
-}
-
-/* check whether Intel graphics is present and reachable */
-static int i915_gfx_present(struct pci_dev *hdac_pci)
-{
-       /* List of known platforms with no i915 support. */
-       static const struct pci_device_id denylist[] = {
-               /* CNL */
-               { PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x5a40), 0x030000, 0xff0000 },
-               { PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x5a41), 0x030000, 0xff0000 },
-               { PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x5a42), 0x030000, 0xff0000 },
-               { PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x5a44), 0x030000, 0xff0000 },
-               { PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x5a49), 0x030000, 0xff0000 },
-               { PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x5a4a), 0x030000, 0xff0000 },
-               { PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x5a4c), 0x030000, 0xff0000 },
-               { PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x5a50), 0x030000, 0xff0000 },
-               { PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x5a51), 0x030000, 0xff0000 },
-               { PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x5a52), 0x030000, 0xff0000 },
-               { PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x5a54), 0x030000, 0xff0000 },
-               { PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x5a59), 0x030000, 0xff0000 },
-               { PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x5a5a), 0x030000, 0xff0000 },
-               { PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x5a5c), 0x030000, 0xff0000 },
-               /* LKF */
-               { PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x9840), 0x030000, 0xff0000 },
-               {}
-       };
-       struct pci_dev *display_dev = NULL;
-
-       if (!gpu_bind || (gpu_bind < 0 && video_firmware_drivers_only()))
-               return false;
-
-       for_each_pci_dev(display_dev) {
-               if (display_dev->vendor != PCI_VENDOR_ID_INTEL ||
-                   (display_dev->class >> 16) != PCI_BASE_CLASS_DISPLAY)
-                       continue;
-
-               if (pci_match_id(denylist, display_dev))
-                       continue;
-
-               if (connectivity_check(display_dev, hdac_pci)) {
-                       pci_dev_put(display_dev);
-                       return true;
-               }
-       }
-
-       return false;
-}
-
-/**
- * snd_hdac_i915_init - Initialize i915 audio component
- * @bus: HDA core bus
- *
- * This function is supposed to be used only by a HD-audio controller
- * driver that needs the interaction with i915 graphics.
- *
- * This function initializes and sets up the audio component to communicate
- * with i915 graphics driver.
- *
- * Returns zero for success or a negative error code.
- */
-int snd_hdac_i915_init(struct hdac_bus *bus)
-{
-       struct drm_audio_component *acomp;
-       int err;
-
-       if (!i915_gfx_present(to_pci_dev(bus->dev)))
-               return -ENODEV;
-
-       err = snd_hdac_acomp_init(bus, NULL,
-                                 i915_component_master_match,
-                                 sizeof(struct i915_audio_component) - sizeof(*acomp));
-       if (err < 0)
-               return err;
-       acomp = bus->audio_component;
-       if (!acomp)
-               return -ENODEV;
-       if (!acomp->ops) {
-               snd_hdac_acomp_exit(bus);
-               return dev_err_probe(bus->dev, -EPROBE_DEFER,
-                                    "couldn't bind with audio component\n");
-       }
-       return 0;
-}
-EXPORT_SYMBOL_GPL(snd_hdac_i915_init);
diff --git a/sound/hda/hdac_regmap.c b/sound/hda/hdac_regmap.c
deleted file mode 100644 (file)
index 97cee09..0000000
+++ /dev/null
@@ -1,601 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0-only
-/*
- * Regmap support for HD-audio verbs
- *
- * A virtual register is translated to one or more hda verbs for write,
- * vice versa for read.
- *
- * A few limitations:
- * - Provided for not all verbs but only subset standard non-volatile verbs.
- * - For reading, only AC_VERB_GET_* variants can be used.
- * - For writing, mapped to the *corresponding* AC_VERB_SET_* variants,
- *   so can't handle asymmetric verbs for read and write
- */
-
-#include <linux/slab.h>
-#include <linux/device.h>
-#include <linux/regmap.h>
-#include <linux/export.h>
-#include <linux/pm.h>
-#include <sound/core.h>
-#include <sound/hdaudio.h>
-#include <sound/hda_regmap.h>
-#include "local.h"
-
-static int codec_pm_lock(struct hdac_device *codec)
-{
-       return snd_hdac_keep_power_up(codec);
-}
-
-static void codec_pm_unlock(struct hdac_device *codec, int lock)
-{
-       if (lock == 1)
-               snd_hdac_power_down_pm(codec);
-}
-
-#define get_verb(reg)  (((reg) >> 8) & 0xfff)
-
-static bool hda_volatile_reg(struct device *dev, unsigned int reg)
-{
-       struct hdac_device *codec = dev_to_hdac_dev(dev);
-       unsigned int verb = get_verb(reg);
-
-       switch (verb) {
-       case AC_VERB_GET_PROC_COEF:
-               return !codec->cache_coef;
-       case AC_VERB_GET_COEF_INDEX:
-       case AC_VERB_GET_PROC_STATE:
-       case AC_VERB_GET_POWER_STATE:
-       case AC_VERB_GET_PIN_SENSE:
-       case AC_VERB_GET_HDMI_DIP_SIZE:
-       case AC_VERB_GET_HDMI_ELDD:
-       case AC_VERB_GET_HDMI_DIP_INDEX:
-       case AC_VERB_GET_HDMI_DIP_DATA:
-       case AC_VERB_GET_HDMI_DIP_XMIT:
-       case AC_VERB_GET_HDMI_CP_CTRL:
-       case AC_VERB_GET_HDMI_CHAN_SLOT:
-       case AC_VERB_GET_DEVICE_SEL:
-       case AC_VERB_GET_DEVICE_LIST:   /* read-only volatile */
-               return true;
-       }
-
-       return false;
-}
-
-static bool hda_writeable_reg(struct device *dev, unsigned int reg)
-{
-       struct hdac_device *codec = dev_to_hdac_dev(dev);
-       unsigned int verb = get_verb(reg);
-       const unsigned int *v;
-       int i;
-
-       snd_array_for_each(&codec->vendor_verbs, i, v) {
-               if (verb == *v)
-                       return true;
-       }
-
-       if (codec->caps_overwriting)
-               return true;
-
-       switch (verb & 0xf00) {
-       case AC_VERB_GET_STREAM_FORMAT:
-       case AC_VERB_GET_AMP_GAIN_MUTE:
-               return true;
-       case AC_VERB_GET_PROC_COEF:
-               return codec->cache_coef;
-       case 0xf00:
-               break;
-       default:
-               return false;
-       }
-
-       switch (verb) {
-       case AC_VERB_GET_CONNECT_SEL:
-       case AC_VERB_GET_SDI_SELECT:
-       case AC_VERB_GET_PIN_WIDGET_CONTROL:
-       case AC_VERB_GET_UNSOLICITED_RESPONSE: /* only as SET_UNSOLICITED_ENABLE */
-       case AC_VERB_GET_BEEP_CONTROL:
-       case AC_VERB_GET_EAPD_BTLENABLE:
-       case AC_VERB_GET_DIGI_CONVERT_1:
-       case AC_VERB_GET_DIGI_CONVERT_2: /* only for beep control */
-       case AC_VERB_GET_VOLUME_KNOB_CONTROL:
-       case AC_VERB_GET_GPIO_MASK:
-       case AC_VERB_GET_GPIO_DIRECTION:
-       case AC_VERB_GET_GPIO_DATA: /* not for volatile read */
-       case AC_VERB_GET_GPIO_WAKE_MASK:
-       case AC_VERB_GET_GPIO_UNSOLICITED_RSP_MASK:
-       case AC_VERB_GET_GPIO_STICKY_MASK:
-               return true;
-       }
-
-       return false;
-}
-
-static bool hda_readable_reg(struct device *dev, unsigned int reg)
-{
-       struct hdac_device *codec = dev_to_hdac_dev(dev);
-       unsigned int verb = get_verb(reg);
-
-       if (codec->caps_overwriting)
-               return true;
-
-       switch (verb) {
-       case AC_VERB_PARAMETERS:
-       case AC_VERB_GET_CONNECT_LIST:
-       case AC_VERB_GET_SUBSYSTEM_ID:
-               return true;
-       /* below are basically writable, but disabled for reducing unnecessary
-        * writes at sync
-        */
-       case AC_VERB_GET_CONFIG_DEFAULT: /* usually just read */
-       case AC_VERB_GET_CONV: /* managed in PCM code */
-       case AC_VERB_GET_CVT_CHAN_COUNT: /* managed in HDMI CA code */
-               return true;
-       }
-
-       return hda_writeable_reg(dev, reg);
-}
-
-/*
- * Stereo amp pseudo register:
- * for making easier to handle the stereo volume control, we provide a
- * fake register to deal both left and right channels by a single
- * (pseudo) register access.  A verb consisting of SET_AMP_GAIN with
- * *both* SET_LEFT and SET_RIGHT bits takes a 16bit value, the lower 8bit
- * for the left and the upper 8bit for the right channel.
- */
-static bool is_stereo_amp_verb(unsigned int reg)
-{
-       if (((reg >> 8) & 0x700) != AC_VERB_SET_AMP_GAIN_MUTE)
-               return false;
-       return (reg & (AC_AMP_SET_LEFT | AC_AMP_SET_RIGHT)) ==
-               (AC_AMP_SET_LEFT | AC_AMP_SET_RIGHT);
-}
-
-/* read a pseudo stereo amp register (16bit left+right) */
-static int hda_reg_read_stereo_amp(struct hdac_device *codec,
-                                  unsigned int reg, unsigned int *val)
-{
-       unsigned int left, right;
-       int err;
-
-       reg &= ~(AC_AMP_SET_LEFT | AC_AMP_SET_RIGHT);
-       err = snd_hdac_exec_verb(codec, reg | AC_AMP_GET_LEFT, 0, &left);
-       if (err < 0)
-               return err;
-       err = snd_hdac_exec_verb(codec, reg | AC_AMP_GET_RIGHT, 0, &right);
-       if (err < 0)
-               return err;
-       *val = left | (right << 8);
-       return 0;
-}
-
-/* write a pseudo stereo amp register (16bit left+right) */
-static int hda_reg_write_stereo_amp(struct hdac_device *codec,
-                                   unsigned int reg, unsigned int val)
-{
-       int err;
-       unsigned int verb, left, right;
-
-       verb = AC_VERB_SET_AMP_GAIN_MUTE << 8;
-       if (reg & AC_AMP_GET_OUTPUT)
-               verb |= AC_AMP_SET_OUTPUT;
-       else
-               verb |= AC_AMP_SET_INPUT | ((reg & 0xf) << 8);
-       reg = (reg & ~0xfffff) | verb;
-
-       left = val & 0xff;
-       right = (val >> 8) & 0xff;
-       if (left == right) {
-               reg |= AC_AMP_SET_LEFT | AC_AMP_SET_RIGHT;
-               return snd_hdac_exec_verb(codec, reg | left, 0, NULL);
-       }
-
-       err = snd_hdac_exec_verb(codec, reg | AC_AMP_SET_LEFT | left, 0, NULL);
-       if (err < 0)
-               return err;
-       err = snd_hdac_exec_verb(codec, reg | AC_AMP_SET_RIGHT | right, 0, NULL);
-       if (err < 0)
-               return err;
-       return 0;
-}
-
-/* read a pseudo coef register (16bit) */
-static int hda_reg_read_coef(struct hdac_device *codec, unsigned int reg,
-                            unsigned int *val)
-{
-       unsigned int verb;
-       int err;
-
-       if (!codec->cache_coef)
-               return -EINVAL;
-       /* LSB 8bit = coef index */
-       verb = (reg & ~0xfff00) | (AC_VERB_SET_COEF_INDEX << 8);
-       err = snd_hdac_exec_verb(codec, verb, 0, NULL);
-       if (err < 0)
-               return err;
-       verb = (reg & ~0xfffff) | (AC_VERB_GET_COEF_INDEX << 8);
-       return snd_hdac_exec_verb(codec, verb, 0, val);
-}
-
-/* write a pseudo coef register (16bit) */
-static int hda_reg_write_coef(struct hdac_device *codec, unsigned int reg,
-                             unsigned int val)
-{
-       unsigned int verb;
-       int err;
-
-       if (!codec->cache_coef)
-               return -EINVAL;
-       /* LSB 8bit = coef index */
-       verb = (reg & ~0xfff00) | (AC_VERB_SET_COEF_INDEX << 8);
-       err = snd_hdac_exec_verb(codec, verb, 0, NULL);
-       if (err < 0)
-               return err;
-       verb = (reg & ~0xfffff) | (AC_VERB_GET_COEF_INDEX << 8) |
-               (val & 0xffff);
-       return snd_hdac_exec_verb(codec, verb, 0, NULL);
-}
-
-static int hda_reg_read(void *context, unsigned int reg, unsigned int *val)
-{
-       struct hdac_device *codec = context;
-       int verb = get_verb(reg);
-       int err;
-       int pm_lock = 0;
-
-       if (verb != AC_VERB_GET_POWER_STATE) {
-               pm_lock = codec_pm_lock(codec);
-               if (pm_lock < 0)
-                       return -EAGAIN;
-       }
-       reg |= (codec->addr << 28);
-       if (is_stereo_amp_verb(reg)) {
-               err = hda_reg_read_stereo_amp(codec, reg, val);
-               goto out;
-       }
-       if (verb == AC_VERB_GET_PROC_COEF) {
-               err = hda_reg_read_coef(codec, reg, val);
-               goto out;
-       }
-       if ((verb & 0x700) == AC_VERB_SET_AMP_GAIN_MUTE)
-               reg &= ~AC_AMP_FAKE_MUTE;
-
-       err = snd_hdac_exec_verb(codec, reg, 0, val);
-       if (err < 0)
-               goto out;
-       /* special handling for asymmetric reads */
-       if (verb == AC_VERB_GET_POWER_STATE) {
-               if (*val & AC_PWRST_ERROR)
-                       *val = -1;
-               else /* take only the actual state */
-                       *val = (*val >> 4) & 0x0f;
-       }
- out:
-       codec_pm_unlock(codec, pm_lock);
-       return err;
-}
-
-static int hda_reg_write(void *context, unsigned int reg, unsigned int val)
-{
-       struct hdac_device *codec = context;
-       unsigned int verb;
-       int i, bytes, err;
-       int pm_lock = 0;
-
-       if (codec->caps_overwriting)
-               return 0;
-
-       reg &= ~0x00080000U; /* drop GET bit */
-       reg |= (codec->addr << 28);
-       verb = get_verb(reg);
-
-       if (verb != AC_VERB_SET_POWER_STATE) {
-               pm_lock = codec_pm_lock(codec);
-               if (pm_lock < 0)
-                       return codec->lazy_cache ? 0 : -EAGAIN;
-       }
-
-       if (is_stereo_amp_verb(reg)) {
-               err = hda_reg_write_stereo_amp(codec, reg, val);
-               goto out;
-       }
-
-       if (verb == AC_VERB_SET_PROC_COEF) {
-               err = hda_reg_write_coef(codec, reg, val);
-               goto out;
-       }
-
-       switch (verb & 0xf00) {
-       case AC_VERB_SET_AMP_GAIN_MUTE:
-               if ((reg & AC_AMP_FAKE_MUTE) && (val & AC_AMP_MUTE))
-                       val = 0;
-               verb = AC_VERB_SET_AMP_GAIN_MUTE;
-               if (reg & AC_AMP_GET_LEFT)
-                       verb |= AC_AMP_SET_LEFT >> 8;
-               else
-                       verb |= AC_AMP_SET_RIGHT >> 8;
-               if (reg & AC_AMP_GET_OUTPUT) {
-                       verb |= AC_AMP_SET_OUTPUT >> 8;
-               } else {
-                       verb |= AC_AMP_SET_INPUT >> 8;
-                       verb |= reg & 0xf;
-               }
-               break;
-       }
-
-       switch (verb) {
-       case AC_VERB_SET_DIGI_CONVERT_1:
-               bytes = 2;
-               break;
-       case AC_VERB_SET_CONFIG_DEFAULT_BYTES_0:
-               bytes = 4;
-               break;
-       default:
-               bytes = 1;
-               break;
-       }
-
-       for (i = 0; i < bytes; i++) {
-               reg &= ~0xfffff;
-               reg |= (verb + i) << 8 | ((val >> (8 * i)) & 0xff);
-               err = snd_hdac_exec_verb(codec, reg, 0, NULL);
-               if (err < 0)
-                       goto out;
-       }
-
- out:
-       codec_pm_unlock(codec, pm_lock);
-       return err;
-}
-
-static const struct regmap_config hda_regmap_cfg = {
-       .name = "hdaudio",
-       .reg_bits = 32,
-       .val_bits = 32,
-       .max_register = 0xfffffff,
-       .writeable_reg = hda_writeable_reg,
-       .readable_reg = hda_readable_reg,
-       .volatile_reg = hda_volatile_reg,
-       .cache_type = REGCACHE_MAPLE,
-       .reg_read = hda_reg_read,
-       .reg_write = hda_reg_write,
-       .use_single_read = true,
-       .use_single_write = true,
-       .disable_locking = true,
-};
-
-/**
- * snd_hdac_regmap_init - Initialize regmap for HDA register accesses
- * @codec: the codec object
- *
- * Returns zero for success or a negative error code.
- */
-int snd_hdac_regmap_init(struct hdac_device *codec)
-{
-       struct regmap *regmap;
-
-       regmap = regmap_init(&codec->dev, NULL, codec, &hda_regmap_cfg);
-       if (IS_ERR(regmap))
-               return PTR_ERR(regmap);
-       codec->regmap = regmap;
-       snd_array_init(&codec->vendor_verbs, sizeof(unsigned int), 8);
-       return 0;
-}
-EXPORT_SYMBOL_GPL(snd_hdac_regmap_init);
-
-/**
- * snd_hdac_regmap_exit - Release the regmap from HDA codec
- * @codec: the codec object
- */
-void snd_hdac_regmap_exit(struct hdac_device *codec)
-{
-       if (codec->regmap) {
-               regmap_exit(codec->regmap);
-               codec->regmap = NULL;
-               snd_array_free(&codec->vendor_verbs);
-       }
-}
-EXPORT_SYMBOL_GPL(snd_hdac_regmap_exit);
-
-/**
- * snd_hdac_regmap_add_vendor_verb - add a vendor-specific verb to regmap
- * @codec: the codec object
- * @verb: verb to allow accessing via regmap
- *
- * Returns zero for success or a negative error code.
- */
-int snd_hdac_regmap_add_vendor_verb(struct hdac_device *codec,
-                                   unsigned int verb)
-{
-       unsigned int *p = snd_array_new(&codec->vendor_verbs);
-
-       if (!p)
-               return -ENOMEM;
-       *p = verb | 0x800; /* set GET bit */
-       return 0;
-}
-EXPORT_SYMBOL_GPL(snd_hdac_regmap_add_vendor_verb);
-
-/*
- * helper functions
- */
-
-/* write a pseudo-register value (w/o power sequence) */
-static int reg_raw_write(struct hdac_device *codec, unsigned int reg,
-                        unsigned int val)
-{
-       int err;
-
-       mutex_lock(&codec->regmap_lock);
-       if (!codec->regmap)
-               err = hda_reg_write(codec, reg, val);
-       else
-               err = regmap_write(codec->regmap, reg, val);
-       mutex_unlock(&codec->regmap_lock);
-       return err;
-}
-
-/* a helper macro to call @func_call; retry with power-up if failed */
-#define CALL_RAW_FUNC(codec, func_call)                                \
-       ({                                                      \
-               int _err = func_call;                           \
-               if (_err == -EAGAIN) {                          \
-                       _err = snd_hdac_power_up_pm(codec);     \
-                       if (_err >= 0)                          \
-                               _err = func_call;               \
-                       snd_hdac_power_down_pm(codec);          \
-               }                                               \
-               _err;})
-
-/**
- * snd_hdac_regmap_write_raw - write a pseudo register with power mgmt
- * @codec: the codec object
- * @reg: pseudo register
- * @val: value to write
- *
- * Returns zero if successful or a negative error code.
- */
-int snd_hdac_regmap_write_raw(struct hdac_device *codec, unsigned int reg,
-                             unsigned int val)
-{
-       return CALL_RAW_FUNC(codec, reg_raw_write(codec, reg, val));
-}
-EXPORT_SYMBOL_GPL(snd_hdac_regmap_write_raw);
-
-static int reg_raw_read(struct hdac_device *codec, unsigned int reg,
-                       unsigned int *val, bool uncached)
-{
-       int err;
-
-       mutex_lock(&codec->regmap_lock);
-       if (uncached || !codec->regmap)
-               err = hda_reg_read(codec, reg, val);
-       else
-               err = regmap_read(codec->regmap, reg, val);
-       mutex_unlock(&codec->regmap_lock);
-       return err;
-}
-
-static int __snd_hdac_regmap_read_raw(struct hdac_device *codec,
-                                     unsigned int reg, unsigned int *val,
-                                     bool uncached)
-{
-       return CALL_RAW_FUNC(codec, reg_raw_read(codec, reg, val, uncached));
-}
-
-/**
- * snd_hdac_regmap_read_raw - read a pseudo register with power mgmt
- * @codec: the codec object
- * @reg: pseudo register
- * @val: pointer to store the read value
- *
- * Returns zero if successful or a negative error code.
- */
-int snd_hdac_regmap_read_raw(struct hdac_device *codec, unsigned int reg,
-                            unsigned int *val)
-{
-       return __snd_hdac_regmap_read_raw(codec, reg, val, false);
-}
-EXPORT_SYMBOL_GPL(snd_hdac_regmap_read_raw);
-
-/* Works like snd_hdac_regmap_read_raw(), but this doesn't read from the
- * cache but always via hda verbs.
- */
-int snd_hdac_regmap_read_raw_uncached(struct hdac_device *codec,
-                                     unsigned int reg, unsigned int *val)
-{
-       return __snd_hdac_regmap_read_raw(codec, reg, val, true);
-}
-
-static int reg_raw_update(struct hdac_device *codec, unsigned int reg,
-                         unsigned int mask, unsigned int val)
-{
-       unsigned int orig;
-       bool change;
-       int err;
-
-       mutex_lock(&codec->regmap_lock);
-       if (codec->regmap) {
-               err = regmap_update_bits_check(codec->regmap, reg, mask, val,
-                                              &change);
-               if (!err)
-                       err = change ? 1 : 0;
-       } else {
-               err = hda_reg_read(codec, reg, &orig);
-               if (!err) {
-                       val &= mask;
-                       val |= orig & ~mask;
-                       if (val != orig) {
-                               err = hda_reg_write(codec, reg, val);
-                               if (!err)
-                                       err = 1;
-                       }
-               }
-       }
-       mutex_unlock(&codec->regmap_lock);
-       return err;
-}
-
-/**
- * snd_hdac_regmap_update_raw - update a pseudo register with power mgmt
- * @codec: the codec object
- * @reg: pseudo register
- * @mask: bit mask to update
- * @val: value to update
- *
- * Returns zero if successful or a negative error code.
- */
-int snd_hdac_regmap_update_raw(struct hdac_device *codec, unsigned int reg,
-                              unsigned int mask, unsigned int val)
-{
-       return CALL_RAW_FUNC(codec, reg_raw_update(codec, reg, mask, val));
-}
-EXPORT_SYMBOL_GPL(snd_hdac_regmap_update_raw);
-
-static int reg_raw_update_once(struct hdac_device *codec, unsigned int reg,
-                              unsigned int mask, unsigned int val)
-{
-       int err = 0;
-
-       if (!codec->regmap)
-               return reg_raw_update(codec, reg, mask, val);
-
-       mutex_lock(&codec->regmap_lock);
-       /* Discard any updates to already initialised registers. */
-       if (!regcache_reg_cached(codec->regmap, reg))
-               err = regmap_update_bits(codec->regmap, reg, mask, val);
-       mutex_unlock(&codec->regmap_lock);
-       return err;
-}
-
-/**
- * snd_hdac_regmap_update_raw_once - initialize the register value only once
- * @codec: the codec object
- * @reg: pseudo register
- * @mask: bit mask to update
- * @val: value to update
- *
- * Performs the update of the register bits only once when the register
- * hasn't been initialized yet.  Used in HD-audio legacy driver.
- * Returns zero if successful or a negative error code
- */
-int snd_hdac_regmap_update_raw_once(struct hdac_device *codec, unsigned int reg,
-                                   unsigned int mask, unsigned int val)
-{
-       return CALL_RAW_FUNC(codec, reg_raw_update_once(codec, reg, mask, val));
-}
-EXPORT_SYMBOL_GPL(snd_hdac_regmap_update_raw_once);
-
-/**
- * snd_hdac_regmap_sync - sync out the cached values for PM resume
- * @codec: the codec object
- */
-void snd_hdac_regmap_sync(struct hdac_device *codec)
-{
-       mutex_lock(&codec->regmap_lock);
-       if (codec->regmap)
-               regcache_sync(codec->regmap);
-       mutex_unlock(&codec->regmap_lock);
-}
-EXPORT_SYMBOL_GPL(snd_hdac_regmap_sync);
diff --git a/sound/hda/hdac_stream.c b/sound/hda/hdac_stream.c
deleted file mode 100644 (file)
index e7f6208..0000000
+++ /dev/null
@@ -1,1014 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0-only
-/*
- * HD-audio stream operations
- */
-
-#include <linux/kernel.h>
-#include <linux/delay.h>
-#include <linux/export.h>
-#include <linux/clocksource.h>
-#include <sound/compress_driver.h>
-#include <sound/core.h>
-#include <sound/pcm.h>
-#include <sound/hdaudio.h>
-#include <sound/hda_register.h>
-#include "trace.h"
-
-/*
- * the hdac_stream library is intended to be used with the following
- * transitions. The states are not formally defined in the code but loosely
- * inspired by boolean variables. Note that the 'prepared' field is not used
- * in this library but by the callers during the hw_params/prepare transitions
- *
- *                        |
- *     stream_init()      |
- *                        v
- *                     +--+-------+
- *                     |  unused  |
- *                     +--+----+--+
- *                        |    ^
- *     stream_assign()    |    |    stream_release()
- *                        v    |
- *                     +--+----+--+
- *                     |  opened  |
- *                     +--+----+--+
- *                        |    ^
- *     stream_reset()     |    |
- *     stream_setup()     |    |    stream_cleanup()
- *                        v    |
- *                     +--+----+--+
- *                     | prepared |
- *                     +--+----+--+
- *                        |    ^
- *     stream_start()     |    |    stream_stop()
- *                        v    |
- *                     +--+----+--+
- *                     |  running |
- *                     +----------+
- */
-
-/**
- * snd_hdac_get_stream_stripe_ctl - get stripe control value
- * @bus: HD-audio core bus
- * @substream: PCM substream
- */
-int snd_hdac_get_stream_stripe_ctl(struct hdac_bus *bus,
-                                  struct snd_pcm_substream *substream)
-{
-       struct snd_pcm_runtime *runtime = substream->runtime;
-       unsigned int channels = runtime->channels,
-                    rate = runtime->rate,
-                    bits_per_sample = runtime->sample_bits,
-                    max_sdo_lines, value, sdo_line;
-
-       /* T_AZA_GCAP_NSDO is 1:2 bitfields in GCAP */
-       max_sdo_lines = snd_hdac_chip_readl(bus, GCAP) & AZX_GCAP_NSDO;
-
-       /* following is from HD audio spec */
-       for (sdo_line = max_sdo_lines; sdo_line > 0; sdo_line >>= 1) {
-               if (rate > 48000)
-                       value = (channels * bits_per_sample *
-                                       (rate / 48000)) / sdo_line;
-               else
-                       value = (channels * bits_per_sample) / sdo_line;
-
-               if (value >= bus->sdo_limit)
-                       break;
-       }
-
-       /* stripe value: 0 for 1SDO, 1 for 2SDO, 2 for 4SDO lines */
-       return sdo_line >> 1;
-}
-EXPORT_SYMBOL_GPL(snd_hdac_get_stream_stripe_ctl);
-
-/**
- * snd_hdac_stream_init - initialize each stream (aka device)
- * @bus: HD-audio core bus
- * @azx_dev: HD-audio 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
- *
- * Assign the starting bdl address to each stream (device) and initialize.
- */
-void snd_hdac_stream_init(struct hdac_bus *bus, struct hdac_stream *azx_dev,
-                         int idx, int direction, int tag)
-{
-       azx_dev->bus = bus;
-       /* offset: SDI0=0x80, SDI1=0xa0, ... SDO3=0x160 */
-       azx_dev->sd_addr = bus->remap_addr + (0x20 * idx + 0x80);
-       /* int mask: SDI0=0x01, SDI1=0x02, ... SDO3=0x80 */
-       azx_dev->sd_int_sta_mask = 1 << idx;
-       azx_dev->index = idx;
-       azx_dev->direction = direction;
-       azx_dev->stream_tag = tag;
-       snd_hdac_dsp_lock_init(azx_dev);
-       list_add_tail(&azx_dev->list, &bus->stream_list);
-
-       if (bus->spbcap) {
-               azx_dev->spib_addr = bus->spbcap + AZX_SPB_BASE +
-                                       AZX_SPB_INTERVAL * idx +
-                                       AZX_SPB_SPIB;
-
-               azx_dev->fifo_addr = bus->spbcap + AZX_SPB_BASE +
-                                       AZX_SPB_INTERVAL * idx +
-                                       AZX_SPB_MAXFIFO;
-       }
-
-       if (bus->drsmcap)
-               azx_dev->dpibr_addr = bus->drsmcap + AZX_DRSM_BASE +
-                                       AZX_DRSM_INTERVAL * idx;
-}
-EXPORT_SYMBOL_GPL(snd_hdac_stream_init);
-
-/**
- * snd_hdac_stream_start - start a stream
- * @azx_dev: HD-audio core stream to start
- *
- * Start a stream, set start_wallclk and set the running flag.
- */
-void snd_hdac_stream_start(struct hdac_stream *azx_dev)
-{
-       struct hdac_bus *bus = azx_dev->bus;
-       int stripe_ctl;
-
-       trace_snd_hdac_stream_start(bus, azx_dev);
-
-       azx_dev->start_wallclk = snd_hdac_chip_readl(bus, WALLCLK);
-
-       /* enable SIE */
-       snd_hdac_chip_updatel(bus, INTCTL,
-                             1 << azx_dev->index,
-                             1 << azx_dev->index);
-       /* set stripe control */
-       if (azx_dev->stripe) {
-               if (azx_dev->substream)
-                       stripe_ctl = snd_hdac_get_stream_stripe_ctl(bus, azx_dev->substream);
-               else
-                       stripe_ctl = 0;
-               snd_hdac_stream_updateb(azx_dev, SD_CTL_3B, SD_CTL_STRIPE_MASK,
-                                       stripe_ctl);
-       }
-       /* set DMA start and interrupt mask */
-       if (bus->access_sdnctl_in_dword)
-               snd_hdac_stream_updatel(azx_dev, SD_CTL,
-                               0, SD_CTL_DMA_START | SD_INT_MASK);
-       else
-               snd_hdac_stream_updateb(azx_dev, SD_CTL,
-                               0, SD_CTL_DMA_START | SD_INT_MASK);
-       azx_dev->running = true;
-}
-EXPORT_SYMBOL_GPL(snd_hdac_stream_start);
-
-/**
- * snd_hdac_stream_clear - helper to clear stream registers and stop DMA transfers
- * @azx_dev: HD-audio core stream to stop
- */
-static void snd_hdac_stream_clear(struct hdac_stream *azx_dev)
-{
-       snd_hdac_stream_updateb(azx_dev, SD_CTL,
-                               SD_CTL_DMA_START | SD_INT_MASK, 0);
-       snd_hdac_stream_writeb(azx_dev, SD_STS, SD_INT_MASK); /* to be sure */
-       if (azx_dev->stripe)
-               snd_hdac_stream_updateb(azx_dev, SD_CTL_3B, SD_CTL_STRIPE_MASK, 0);
-       azx_dev->running = false;
-}
-
-/**
- * snd_hdac_stream_stop - stop a stream
- * @azx_dev: HD-audio core stream to stop
- *
- * Stop a stream DMA and disable stream interrupt
- */
-void snd_hdac_stream_stop(struct hdac_stream *azx_dev)
-{
-       trace_snd_hdac_stream_stop(azx_dev->bus, azx_dev);
-
-       snd_hdac_stream_clear(azx_dev);
-       /* disable SIE */
-       snd_hdac_chip_updatel(azx_dev->bus, INTCTL, 1 << azx_dev->index, 0);
-}
-EXPORT_SYMBOL_GPL(snd_hdac_stream_stop);
-
-/**
- * snd_hdac_stop_streams - stop all streams
- * @bus: HD-audio core bus
- */
-void snd_hdac_stop_streams(struct hdac_bus *bus)
-{
-       struct hdac_stream *stream;
-
-       list_for_each_entry(stream, &bus->stream_list, list)
-               snd_hdac_stream_stop(stream);
-}
-EXPORT_SYMBOL_GPL(snd_hdac_stop_streams);
-
-/**
- * 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)
-{
-
-       if (bus->chip_init) {
-               snd_hdac_stop_streams(bus);
-               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
- */
-void snd_hdac_stream_reset(struct hdac_stream *azx_dev)
-{
-       unsigned char val;
-       int dma_run_state;
-
-       snd_hdac_stream_clear(azx_dev);
-
-       dma_run_state = snd_hdac_stream_readb(azx_dev, SD_CTL) & SD_CTL_DMA_START;
-
-       snd_hdac_stream_updateb(azx_dev, SD_CTL, 0, SD_CTL_STREAM_RESET);
-
-       /* wait for hardware to report that the stream entered reset */
-       snd_hdac_stream_readb_poll(azx_dev, SD_CTL, val, (val & SD_CTL_STREAM_RESET), 3, 300);
-
-       if (azx_dev->bus->dma_stop_delay && dma_run_state)
-               udelay(azx_dev->bus->dma_stop_delay);
-
-       snd_hdac_stream_updateb(azx_dev, SD_CTL, SD_CTL_STREAM_RESET, 0);
-
-       /* wait for hardware to report that the stream is out of reset */
-       snd_hdac_stream_readb_poll(azx_dev, SD_CTL, val, !(val & SD_CTL_STREAM_RESET), 3, 300);
-
-       /* reset first position - may not be synced with hw at this time */
-       if (azx_dev->posbuf)
-               *azx_dev->posbuf = 0;
-}
-EXPORT_SYMBOL_GPL(snd_hdac_stream_reset);
-
-/**
- * snd_hdac_stream_setup -  set up the SD for streaming
- * @azx_dev: HD-audio core stream to set up
- * @code_loading: Whether the stream is for PCM or code-loading.
- */
-int snd_hdac_stream_setup(struct hdac_stream *azx_dev, bool code_loading)
-{
-       struct hdac_bus *bus = azx_dev->bus;
-       struct snd_pcm_runtime *runtime;
-       unsigned int val;
-       u16 reg;
-       int ret;
-
-       if (azx_dev->substream)
-               runtime = azx_dev->substream->runtime;
-       else
-               runtime = NULL;
-       /* make sure the run bit is zero for SD */
-       snd_hdac_stream_clear(azx_dev);
-       /* program the stream_tag */
-       val = snd_hdac_stream_readl(azx_dev, SD_CTL);
-       val = (val & ~SD_CTL_STREAM_TAG_MASK) |
-               (azx_dev->stream_tag << SD_CTL_STREAM_TAG_SHIFT);
-       if (!bus->snoop)
-               val |= SD_CTL_TRAFFIC_PRIO;
-       snd_hdac_stream_writel(azx_dev, SD_CTL, val);
-
-       /* program the length of samples in cyclic buffer */
-       snd_hdac_stream_writel(azx_dev, SD_CBL, azx_dev->bufsize);
-
-       /* program the stream format */
-       /* this value needs to be the same as the one programmed */
-       snd_hdac_stream_writew(azx_dev, SD_FORMAT, azx_dev->format_val);
-
-       /* program the stream LVI (last valid index) of the BDL */
-       snd_hdac_stream_writew(azx_dev, SD_LVI, azx_dev->frags - 1);
-
-       /* program the BDL address */
-       /* lower BDL address */
-       snd_hdac_stream_writel(azx_dev, SD_BDLPL, (u32)azx_dev->bdl.addr);
-       /* upper BDL address */
-       snd_hdac_stream_writel(azx_dev, SD_BDLPU,
-                              upper_32_bits(azx_dev->bdl.addr));
-
-       /* enable the position buffer */
-       if (bus->use_posbuf && bus->posbuf.addr) {
-               if (!(snd_hdac_chip_readl(bus, DPLBASE) & AZX_DPLBASE_ENABLE))
-                       snd_hdac_chip_writel(bus, DPLBASE,
-                               (u32)bus->posbuf.addr | AZX_DPLBASE_ENABLE);
-       }
-
-       /* set the interrupt enable bits in the descriptor control register */
-       snd_hdac_stream_updatel(azx_dev, SD_CTL, 0, SD_INT_MASK);
-
-       if (!code_loading) {
-               /* Once SDxFMT is set, the controller programs SDxFIFOS to non-zero value. */
-               ret = snd_hdac_stream_readw_poll(azx_dev, SD_FIFOSIZE, reg,
-                                                reg & AZX_SD_FIFOSIZE_MASK, 3, 300);
-               if (ret)
-                       dev_dbg(bus->dev, "polling SD_FIFOSIZE 0x%04x failed: %d\n",
-                               AZX_REG_SD_FIFOSIZE, ret);
-               azx_dev->fifo_size = reg;
-       }
-
-       /* when LPIB delay correction gives a small negative value,
-        * we ignore it; currently set the threshold statically to
-        * 64 frames
-        */
-       if (runtime && runtime->period_size > 64)
-               azx_dev->delay_negative_threshold =
-                       -frames_to_bytes(runtime, 64);
-       else
-               azx_dev->delay_negative_threshold = 0;
-
-       /* wallclk has 24Mhz clock source */
-       if (runtime)
-               azx_dev->period_wallclk = (((runtime->period_size * 24000) /
-                                   runtime->rate) * 1000);
-
-       return 0;
-}
-EXPORT_SYMBOL_GPL(snd_hdac_stream_setup);
-
-/**
- * snd_hdac_stream_cleanup - cleanup a stream
- * @azx_dev: HD-audio core stream to clean up
- */
-void snd_hdac_stream_cleanup(struct hdac_stream *azx_dev)
-{
-       snd_hdac_stream_writel(azx_dev, SD_BDLPL, 0);
-       snd_hdac_stream_writel(azx_dev, SD_BDLPU, 0);
-       snd_hdac_stream_writel(azx_dev, SD_CTL, 0);
-       azx_dev->bufsize = 0;
-       azx_dev->period_bytes = 0;
-       azx_dev->format_val = 0;
-}
-EXPORT_SYMBOL_GPL(snd_hdac_stream_cleanup);
-
-/**
- * snd_hdac_stream_assign - assign a stream for the PCM
- * @bus: HD-audio core bus
- * @substream: PCM substream to assign
- *
- * Look for an unused stream for the given PCM substream, assign it
- * and return the stream object.  If no stream is free, returns NULL.
- * The function tries to keep using the same stream object when it's used
- * beforehand.  Also, when bus->reverse_assign flag is set, the last free
- * or matching entry is returned.  This is needed for some strange codecs.
- */
-struct hdac_stream *snd_hdac_stream_assign(struct hdac_bus *bus,
-                                          struct snd_pcm_substream *substream)
-{
-       struct hdac_stream *azx_dev;
-       struct hdac_stream *res = NULL;
-
-       /* make a non-zero unique key for the substream */
-       int key = (substream->number << 2) | (substream->stream + 1);
-
-       if (substream->pcm)
-               key |= (substream->pcm->device << 16);
-
-       spin_lock_irq(&bus->reg_lock);
-       list_for_each_entry(azx_dev, &bus->stream_list, list) {
-               if (azx_dev->direction != substream->stream)
-                       continue;
-               if (azx_dev->opened)
-                       continue;
-               if (azx_dev->assigned_key == key) {
-                       res = azx_dev;
-                       break;
-               }
-               if (!res || bus->reverse_assign)
-                       res = azx_dev;
-       }
-       if (res) {
-               res->opened = 1;
-               res->running = 0;
-               res->assigned_key = key;
-               res->substream = substream;
-       }
-       spin_unlock_irq(&bus->reg_lock);
-       return res;
-}
-EXPORT_SYMBOL_GPL(snd_hdac_stream_assign);
-
-/**
- * snd_hdac_stream_release_locked - release the assigned stream
- * @azx_dev: HD-audio core stream to release
- *
- * Release the stream that has been assigned by snd_hdac_stream_assign().
- * The bus->reg_lock needs to be taken at a higher level
- */
-void snd_hdac_stream_release_locked(struct hdac_stream *azx_dev)
-{
-       azx_dev->opened = 0;
-       azx_dev->running = 0;
-       azx_dev->substream = NULL;
-}
-EXPORT_SYMBOL_GPL(snd_hdac_stream_release_locked);
-
-/**
- * snd_hdac_stream_release - release the assigned stream
- * @azx_dev: HD-audio core stream to release
- *
- * Release the stream that has been assigned by snd_hdac_stream_assign().
- */
-void snd_hdac_stream_release(struct hdac_stream *azx_dev)
-{
-       struct hdac_bus *bus = azx_dev->bus;
-
-       spin_lock_irq(&bus->reg_lock);
-       snd_hdac_stream_release_locked(azx_dev);
-       spin_unlock_irq(&bus->reg_lock);
-}
-EXPORT_SYMBOL_GPL(snd_hdac_stream_release);
-
-/**
- * snd_hdac_get_stream - return hdac_stream based on stream_tag and
- * direction
- *
- * @bus: HD-audio core bus
- * @dir: direction for the stream to be found
- * @stream_tag: stream tag for stream to be found
- */
-struct hdac_stream *snd_hdac_get_stream(struct hdac_bus *bus,
-                                       int dir, int stream_tag)
-{
-       struct hdac_stream *s;
-
-       list_for_each_entry(s, &bus->stream_list, list) {
-               if (s->direction == dir && s->stream_tag == stream_tag)
-                       return s;
-       }
-
-       return NULL;
-}
-EXPORT_SYMBOL_GPL(snd_hdac_get_stream);
-
-/*
- * set up a BDL entry
- */
-static int setup_bdle(struct hdac_bus *bus,
-                     struct snd_dma_buffer *dmab,
-                     struct hdac_stream *azx_dev, __le32 **bdlp,
-                     int ofs, int size, int with_ioc)
-{
-       __le32 *bdl = *bdlp;
-
-       while (size > 0) {
-               dma_addr_t addr;
-               int chunk;
-
-               if (azx_dev->frags >= AZX_MAX_BDL_ENTRIES)
-                       return -EINVAL;
-
-               addr = snd_sgbuf_get_addr(dmab, ofs);
-               /* program the address field of the BDL entry */
-               bdl[0] = cpu_to_le32((u32)addr);
-               bdl[1] = cpu_to_le32(upper_32_bits(addr));
-               /* program the size field of the BDL entry */
-               chunk = snd_sgbuf_get_chunk_size(dmab, ofs, size);
-               /* one BDLE cannot cross 4K boundary on CTHDA chips */
-               if (bus->align_bdle_4k) {
-                       u32 remain = 0x1000 - (ofs & 0xfff);
-
-                       if (chunk > remain)
-                               chunk = remain;
-               }
-               bdl[2] = cpu_to_le32(chunk);
-               /* program the IOC to enable interrupt
-                * only when the whole fragment is processed
-                */
-               size -= chunk;
-               bdl[3] = (size || !with_ioc) ? 0 : cpu_to_le32(0x01);
-               bdl += 4;
-               azx_dev->frags++;
-               ofs += chunk;
-       }
-       *bdlp = bdl;
-       return ofs;
-}
-
-/**
- * snd_hdac_stream_setup_bdle - set up BDL entries
- * @azx_dev: HD-audio core stream to set up
- * @dmab: allocated DMA buffer
- * @runtime: substream runtime, optional
- *
- * Set up the buffer descriptor table of the given stream based on the
- * period and buffer sizes of the assigned PCM substream.
- */
-static int snd_hdac_stream_setup_bdle(struct hdac_stream *azx_dev, struct snd_dma_buffer *dmab,
-                                     struct snd_pcm_runtime *runtime)
-{
-       struct hdac_bus *bus = azx_dev->bus;
-       int i, ofs, periods, period_bytes;
-       int pos_adj, pos_align;
-       __le32 *bdl;
-
-       /* reset BDL address */
-       snd_hdac_stream_writel(azx_dev, SD_BDLPL, 0);
-       snd_hdac_stream_writel(azx_dev, SD_BDLPU, 0);
-
-       period_bytes = azx_dev->period_bytes;
-       periods = azx_dev->bufsize / period_bytes;
-
-       /* program the initial BDL entries */
-       bdl = (__le32 *)azx_dev->bdl.area;
-       ofs = 0;
-       azx_dev->frags = 0;
-
-       pos_adj = bus->bdl_pos_adj;
-       if (runtime && !azx_dev->no_period_wakeup && pos_adj > 0) {
-               pos_align = pos_adj;
-               pos_adj = DIV_ROUND_UP(pos_adj * runtime->rate, 48000);
-               if (!pos_adj)
-                       pos_adj = pos_align;
-               else
-                       pos_adj = roundup(pos_adj, pos_align);
-               pos_adj = frames_to_bytes(runtime, pos_adj);
-               if (pos_adj >= period_bytes) {
-                       dev_warn(bus->dev, "Too big adjustment %d\n",
-                                pos_adj);
-                       pos_adj = 0;
-               } else {
-                       ofs = setup_bdle(bus, dmab, azx_dev,
-                                        &bdl, ofs, pos_adj, true);
-                       if (ofs < 0)
-                               goto error;
-               }
-       } else
-               pos_adj = 0;
-
-       for (i = 0; i < periods; i++) {
-               if (i == periods - 1 && pos_adj)
-                       ofs = setup_bdle(bus, dmab, azx_dev,
-                                        &bdl, ofs, period_bytes - pos_adj, 0);
-               else
-                       ofs = setup_bdle(bus, dmab, azx_dev,
-                                        &bdl, ofs, period_bytes,
-                                        !azx_dev->no_period_wakeup);
-               if (ofs < 0)
-                       goto error;
-       }
-       return 0;
-
- error:
-       dev_dbg(bus->dev, "Too many BDL entries: buffer=%d, period=%d\n",
-               azx_dev->bufsize, period_bytes);
-       return -EINVAL;
-}
-
-/**
- * snd_hdac_stream_setup_periods - set up BDL entries
- * @azx_dev: HD-audio core stream to set up
- *
- * Set up the buffer descriptor table of the given stream based on the
- * period and buffer sizes of the assigned PCM substream.
- */
-int snd_hdac_stream_setup_periods(struct hdac_stream *azx_dev)
-{
-       struct snd_pcm_substream *substream = azx_dev->substream;
-       struct snd_compr_stream *cstream = azx_dev->cstream;
-       struct snd_pcm_runtime *runtime = NULL;
-       struct snd_dma_buffer *dmab;
-
-       if (substream) {
-               runtime = substream->runtime;
-               dmab = snd_pcm_get_dma_buf(substream);
-       } else if (cstream) {
-               dmab = snd_pcm_get_dma_buf(cstream);
-       } else {
-               WARN(1, "No substream or cstream assigned\n");
-               return -EINVAL;
-       }
-
-       return snd_hdac_stream_setup_bdle(azx_dev, dmab, runtime);
-}
-EXPORT_SYMBOL_GPL(snd_hdac_stream_setup_periods);
-
-/**
- * snd_hdac_stream_set_params - set stream parameters
- * @azx_dev: HD-audio core stream for which parameters are to be set
- * @format_val: format value parameter
- *
- * Setup the HD-audio core stream parameters from substream of the stream
- * and passed format value
- */
-int snd_hdac_stream_set_params(struct hdac_stream *azx_dev,
-                                unsigned int format_val)
-{
-       struct snd_pcm_substream *substream = azx_dev->substream;
-       struct snd_compr_stream *cstream = azx_dev->cstream;
-       unsigned int bufsize, period_bytes;
-       unsigned int no_period_wakeup;
-       int err;
-
-       if (substream) {
-               bufsize = snd_pcm_lib_buffer_bytes(substream);
-               period_bytes = snd_pcm_lib_period_bytes(substream);
-               no_period_wakeup = substream->runtime->no_period_wakeup;
-       } else if (cstream) {
-               bufsize = cstream->runtime->buffer_size;
-               period_bytes = cstream->runtime->fragment_size;
-               no_period_wakeup = 0;
-       } else {
-               return -EINVAL;
-       }
-
-       if (bufsize != azx_dev->bufsize ||
-           period_bytes != azx_dev->period_bytes ||
-           format_val != azx_dev->format_val ||
-           no_period_wakeup != azx_dev->no_period_wakeup) {
-               azx_dev->bufsize = bufsize;
-               azx_dev->period_bytes = period_bytes;
-               azx_dev->format_val = format_val;
-               azx_dev->no_period_wakeup = no_period_wakeup;
-               err = snd_hdac_stream_setup_periods(azx_dev);
-               if (err < 0)
-                       return err;
-       }
-       return 0;
-}
-EXPORT_SYMBOL_GPL(snd_hdac_stream_set_params);
-
-static u64 azx_cc_read(const struct cyclecounter *cc)
-{
-       struct hdac_stream *azx_dev = container_of(cc, struct hdac_stream, cc);
-
-       return snd_hdac_chip_readl(azx_dev->bus, WALLCLK);
-}
-
-static void azx_timecounter_init(struct hdac_stream *azx_dev,
-                                bool force, u64 last)
-{
-       struct timecounter *tc = &azx_dev->tc;
-       struct cyclecounter *cc = &azx_dev->cc;
-       u64 nsec;
-
-       cc->read = azx_cc_read;
-       cc->mask = CLOCKSOURCE_MASK(32);
-
-       /*
-        * Calculate the optimal mult/shift values. The counter wraps
-        * around after ~178.9 seconds.
-        */
-       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);
-       if (force) {
-               /*
-                * force timecounter to use predefined value,
-                * used for synchronized starts
-                */
-               tc->cycle_last = last;
-       }
-}
-
-/**
- * snd_hdac_stream_timecounter_init - initialize time counter
- * @azx_dev: HD-audio core stream (master stream)
- * @streams: bit flags of streams to set up
- * @start: true for PCM trigger start, false for other cases
- *
- * Initializes the time counter of streams marked by the bit flags (each
- * bit corresponds to the stream index).
- * The trigger timestamp of PCM substream assigned to the given stream is
- * updated accordingly, too.
- */
-void snd_hdac_stream_timecounter_init(struct hdac_stream *azx_dev,
-                                     unsigned int streams, bool start)
-{
-       struct hdac_bus *bus = azx_dev->bus;
-       struct snd_pcm_runtime *runtime = azx_dev->substream->runtime;
-       struct hdac_stream *s;
-       bool inited = false;
-       u64 cycle_last = 0;
-
-       if (!start)
-               goto skip;
-
-       list_for_each_entry(s, &bus->stream_list, list) {
-               if ((streams & (1 << s->index))) {
-                       azx_timecounter_init(s, inited, cycle_last);
-                       if (!inited) {
-                               inited = true;
-                               cycle_last = s->tc.cycle_last;
-                       }
-               }
-       }
-
-skip:
-       snd_pcm_gettime(runtime, &runtime->trigger_tstamp);
-       runtime->trigger_tstamp_latched = true;
-}
-EXPORT_SYMBOL_GPL(snd_hdac_stream_timecounter_init);
-
-/**
- * snd_hdac_stream_sync_trigger - turn on/off stream sync register
- * @azx_dev: HD-audio core stream (master stream)
- * @set: true = set, false = clear
- * @streams: bit flags of streams to sync
- * @reg: the stream sync register address
- */
-void snd_hdac_stream_sync_trigger(struct hdac_stream *azx_dev, bool set,
-                                 unsigned int streams, unsigned int reg)
-{
-       struct hdac_bus *bus = azx_dev->bus;
-       unsigned int val;
-
-       if (!reg)
-               reg = AZX_REG_SSYNC;
-       val = _snd_hdac_chip_readl(bus, reg);
-       if (set)
-               val |= streams;
-       else
-               val &= ~streams;
-       _snd_hdac_chip_writel(bus, reg, val);
-}
-EXPORT_SYMBOL_GPL(snd_hdac_stream_sync_trigger);
-
-/**
- * snd_hdac_stream_sync - sync with start/stop trigger operation
- * @azx_dev: HD-audio core stream (master stream)
- * @start: true = start, false = stop
- * @streams: bit flags of streams to sync
- *
- * For @start = true, wait until all FIFOs get ready.
- * For @start = false, wait until all RUN bits are cleared.
- */
-void snd_hdac_stream_sync(struct hdac_stream *azx_dev, bool start,
-                         unsigned int streams)
-{
-       struct hdac_bus *bus = azx_dev->bus;
-       int nwait, timeout;
-       struct hdac_stream *s;
-
-       for (timeout = 5000; timeout; timeout--) {
-               nwait = 0;
-               list_for_each_entry(s, &bus->stream_list, list) {
-                       if (!(streams & (1 << s->index)))
-                               continue;
-
-                       if (start) {
-                               /* check FIFO gets ready */
-                               if (!(snd_hdac_stream_readb(s, SD_STS) &
-                                     SD_STS_FIFO_READY))
-                                       nwait++;
-                       } else {
-                               /* check RUN bit is cleared */
-                               if (snd_hdac_stream_readb(s, SD_CTL) &
-                                   SD_CTL_DMA_START) {
-                                       nwait++;
-                                       /*
-                                        * Perform stream reset if DMA RUN
-                                        * bit not cleared within given timeout
-                                        */
-                                       if (timeout == 1)
-                                               snd_hdac_stream_reset(s);
-                               }
-                       }
-               }
-               if (!nwait)
-                       break;
-               cpu_relax();
-       }
-}
-EXPORT_SYMBOL_GPL(snd_hdac_stream_sync);
-
-/**
- * snd_hdac_stream_spbcap_enable - enable SPIB for a stream
- * @bus: HD-audio core bus
- * @enable: flag to enable/disable SPIB
- * @index: stream index for which SPIB need to be enabled
- */
-void snd_hdac_stream_spbcap_enable(struct hdac_bus *bus,
-                                  bool enable, int index)
-{
-       u32 mask = 0;
-
-       if (!bus->spbcap) {
-               dev_err(bus->dev, "Address of SPB capability is NULL\n");
-               return;
-       }
-
-       mask |= (1 << index);
-
-       if (enable)
-               snd_hdac_updatel(bus->spbcap, AZX_REG_SPB_SPBFCCTL, mask, mask);
-       else
-               snd_hdac_updatel(bus->spbcap, AZX_REG_SPB_SPBFCCTL, mask, 0);
-}
-EXPORT_SYMBOL_GPL(snd_hdac_stream_spbcap_enable);
-
-/**
- * snd_hdac_stream_set_spib - sets the spib value of a stream
- * @bus: HD-audio core bus
- * @azx_dev: hdac_stream
- * @value: spib value to set
- */
-int snd_hdac_stream_set_spib(struct hdac_bus *bus,
-                            struct hdac_stream *azx_dev, u32 value)
-{
-       if (!bus->spbcap) {
-               dev_err(bus->dev, "Address of SPB capability is NULL\n");
-               return -EINVAL;
-       }
-
-       writel(value, azx_dev->spib_addr);
-
-       return 0;
-}
-EXPORT_SYMBOL_GPL(snd_hdac_stream_set_spib);
-
-/**
- * snd_hdac_stream_drsm_enable - enable DMA resume for a stream
- * @bus: HD-audio core bus
- * @enable: flag to enable/disable DRSM
- * @index: stream index for which DRSM need to be enabled
- */
-void snd_hdac_stream_drsm_enable(struct hdac_bus *bus,
-                                bool enable, int index)
-{
-       u32 mask = 0;
-
-       if (!bus->drsmcap) {
-               dev_err(bus->dev, "Address of DRSM capability is NULL\n");
-               return;
-       }
-
-       mask |= (1 << index);
-
-       if (enable)
-               snd_hdac_updatel(bus->drsmcap, AZX_REG_DRSM_CTL, mask, mask);
-       else
-               snd_hdac_updatel(bus->drsmcap, AZX_REG_DRSM_CTL, mask, 0);
-}
-EXPORT_SYMBOL_GPL(snd_hdac_stream_drsm_enable);
-
-/*
- * snd_hdac_stream_wait_drsm - wait for HW to clear RSM for a stream
- * @azx_dev: HD-audio core stream to await RSM for
- *
- * Returns 0 on success and -ETIMEDOUT upon a timeout.
- */
-int snd_hdac_stream_wait_drsm(struct hdac_stream *azx_dev)
-{
-       struct hdac_bus *bus = azx_dev->bus;
-       u32 mask, reg;
-       int ret;
-
-       mask = 1 << azx_dev->index;
-
-       ret = read_poll_timeout(snd_hdac_reg_readl, reg, !(reg & mask), 250, 2000, false, bus,
-                               bus->drsmcap + AZX_REG_DRSM_CTL);
-       if (ret)
-               dev_dbg(bus->dev, "polling RSM 0x%08x failed: %d\n", mask, ret);
-       return ret;
-}
-EXPORT_SYMBOL_GPL(snd_hdac_stream_wait_drsm);
-
-/**
- * snd_hdac_stream_set_dpibr - sets the dpibr value of a stream
- * @bus: HD-audio core bus
- * @azx_dev: hdac_stream
- * @value: dpib value to set
- */
-int snd_hdac_stream_set_dpibr(struct hdac_bus *bus,
-                             struct hdac_stream *azx_dev, u32 value)
-{
-       if (!bus->drsmcap) {
-               dev_err(bus->dev, "Address of DRSM capability is NULL\n");
-               return -EINVAL;
-       }
-
-       writel(value, azx_dev->dpibr_addr);
-
-       return 0;
-}
-EXPORT_SYMBOL_GPL(snd_hdac_stream_set_dpibr);
-
-/**
- * snd_hdac_stream_set_lpib - sets the lpib value of a stream
- * @azx_dev: hdac_stream
- * @value: lpib value to set
- */
-int snd_hdac_stream_set_lpib(struct hdac_stream *azx_dev, u32 value)
-{
-       snd_hdac_stream_writel(azx_dev, SD_LPIB, value);
-
-       return 0;
-}
-EXPORT_SYMBOL_GPL(snd_hdac_stream_set_lpib);
-
-#ifdef CONFIG_SND_HDA_DSP_LOADER
-/**
- * snd_hdac_dsp_prepare - prepare for DSP loading
- * @azx_dev: HD-audio core stream used for DSP loading
- * @format: HD-audio stream format
- * @byte_size: data chunk byte size
- * @bufp: allocated buffer
- *
- * Allocate the buffer for the given size and set up the given stream for
- * DSP loading.  Returns the stream tag (>= 0), or a negative error code.
- */
-int snd_hdac_dsp_prepare(struct hdac_stream *azx_dev, unsigned int format,
-                        unsigned int byte_size, struct snd_dma_buffer *bufp)
-{
-       struct hdac_bus *bus = azx_dev->bus;
-       int err;
-
-       snd_hdac_dsp_lock(azx_dev);
-       spin_lock_irq(&bus->reg_lock);
-       if (azx_dev->running || azx_dev->locked) {
-               spin_unlock_irq(&bus->reg_lock);
-               err = -EBUSY;
-               goto unlock;
-       }
-       azx_dev->locked = true;
-       spin_unlock_irq(&bus->reg_lock);
-
-       err = snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV_SG, bus->dev,
-                                 byte_size, bufp);
-       if (err < 0)
-               goto err_alloc;
-
-       azx_dev->substream = NULL;
-       azx_dev->bufsize = byte_size;
-       /* It is recommended to transfer the firmware in two or more chunks. */
-       azx_dev->period_bytes = byte_size / 2;
-       azx_dev->format_val = format;
-       azx_dev->no_period_wakeup = 1;
-
-       snd_hdac_stream_reset(azx_dev);
-
-       err = snd_hdac_stream_setup_bdle(azx_dev, bufp, NULL);
-       if (err < 0)
-               goto error;
-
-       snd_hdac_stream_setup(azx_dev, true);
-       snd_hdac_dsp_unlock(azx_dev);
-       return azx_dev->stream_tag;
-
- error:
-       snd_dma_free_pages(bufp);
- err_alloc:
-       spin_lock_irq(&bus->reg_lock);
-       azx_dev->locked = false;
-       spin_unlock_irq(&bus->reg_lock);
- unlock:
-       snd_hdac_dsp_unlock(azx_dev);
-       return err;
-}
-EXPORT_SYMBOL_GPL(snd_hdac_dsp_prepare);
-
-/**
- * snd_hdac_dsp_trigger - start / stop DSP loading
- * @azx_dev: HD-audio core stream used for DSP loading
- * @start: trigger start or stop
- */
-void snd_hdac_dsp_trigger(struct hdac_stream *azx_dev, bool start)
-{
-       if (start)
-               snd_hdac_stream_start(azx_dev);
-       else
-               snd_hdac_stream_stop(azx_dev);
-}
-EXPORT_SYMBOL_GPL(snd_hdac_dsp_trigger);
-
-/**
- * snd_hdac_dsp_cleanup - clean up the stream from DSP loading to normal
- * @azx_dev: HD-audio core stream used for DSP loading
- * @dmab: buffer used by DSP loading
- */
-void snd_hdac_dsp_cleanup(struct hdac_stream *azx_dev,
-                         struct snd_dma_buffer *dmab)
-{
-       struct hdac_bus *bus = azx_dev->bus;
-
-       if (!dmab->area || !azx_dev->locked)
-               return;
-
-       snd_hdac_dsp_lock(azx_dev);
-       /* reset BDL address */
-       snd_hdac_stream_writel(azx_dev, SD_BDLPL, 0);
-       snd_hdac_stream_writel(azx_dev, SD_BDLPU, 0);
-       snd_hdac_stream_writel(azx_dev, SD_CTL, 0);
-       azx_dev->bufsize = 0;
-       azx_dev->period_bytes = 0;
-       azx_dev->format_val = 0;
-
-       snd_dma_free_pages(dmab);
-       dmab->area = NULL;
-
-       spin_lock_irq(&bus->reg_lock);
-       azx_dev->locked = false;
-       spin_unlock_irq(&bus->reg_lock);
-       snd_hdac_dsp_unlock(azx_dev);
-}
-EXPORT_SYMBOL_GPL(snd_hdac_dsp_cleanup);
-#endif /* CONFIG_SND_HDA_DSP_LOADER */
diff --git a/sound/hda/hdac_sysfs.c b/sound/hda/hdac_sysfs.c
deleted file mode 100644 (file)
index bffe528..0000000
+++ /dev/null
@@ -1,469 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0
-/*
- * sysfs support for HD-audio core device
- */
-
-#include <linux/slab.h>
-#include <linux/sysfs.h>
-#include <linux/device.h>
-#include <sound/core.h>
-#include <sound/hdaudio.h>
-#include "local.h"
-
-struct hdac_widget_tree {
-       struct kobject *root;
-       struct kobject *afg;
-       struct kobject **nodes;
-};
-
-#define CODEC_ATTR(type)                                       \
-static ssize_t type##_show(struct device *dev,                 \
-                          struct device_attribute *attr,       \
-                          char *buf)                           \
-{                                                              \
-       struct hdac_device *codec = dev_to_hdac_dev(dev);       \
-       return sysfs_emit(buf, "0x%x\n", codec->type);          \
-} \
-static DEVICE_ATTR_RO(type)
-
-#define CODEC_ATTR_STR(type)                                   \
-static ssize_t type##_show(struct device *dev,                 \
-                            struct device_attribute *attr,     \
-                                       char *buf)              \
-{                                                              \
-       struct hdac_device *codec = dev_to_hdac_dev(dev);       \
-       return sysfs_emit(buf, "%s\n",                          \
-                         codec->type ? codec->type : "");      \
-} \
-static DEVICE_ATTR_RO(type)
-
-CODEC_ATTR(type);
-CODEC_ATTR(vendor_id);
-CODEC_ATTR(subsystem_id);
-CODEC_ATTR(revision_id);
-CODEC_ATTR(afg);
-CODEC_ATTR(mfg);
-CODEC_ATTR_STR(vendor_name);
-CODEC_ATTR_STR(chip_name);
-
-static ssize_t modalias_show(struct device *dev, struct device_attribute *attr,
-                            char *buf)
-{
-       return snd_hdac_codec_modalias(dev_to_hdac_dev(dev), buf, 256);
-}
-static DEVICE_ATTR_RO(modalias);
-
-static struct attribute *hdac_dev_attrs[] = {
-       &dev_attr_type.attr,
-       &dev_attr_vendor_id.attr,
-       &dev_attr_subsystem_id.attr,
-       &dev_attr_revision_id.attr,
-       &dev_attr_afg.attr,
-       &dev_attr_mfg.attr,
-       &dev_attr_vendor_name.attr,
-       &dev_attr_chip_name.attr,
-       &dev_attr_modalias.attr,
-       NULL
-};
-
-static const struct attribute_group hdac_dev_attr_group = {
-       .attrs  = hdac_dev_attrs,
-};
-
-const struct attribute_group *hdac_dev_attr_groups[] = {
-       &hdac_dev_attr_group,
-       NULL
-};
-
-/*
- * Widget tree sysfs
- *
- * This is a tree showing the attributes of each widget.  It appears like
- * /sys/bus/hdaudioC0D0/widgets/04/caps
- */
-
-struct widget_attribute;
-
-struct widget_attribute {
-       struct attribute        attr;
-       ssize_t (*show)(struct hdac_device *codec, hda_nid_t nid,
-                       struct widget_attribute *attr, char *buf);
-       ssize_t (*store)(struct hdac_device *codec, hda_nid_t nid,
-                        struct widget_attribute *attr,
-                        const char *buf, size_t count);
-};
-
-static int get_codec_nid(struct kobject *kobj, struct hdac_device **codecp)
-{
-       struct device *dev = kobj_to_dev(kobj->parent->parent);
-       int nid;
-       ssize_t ret;
-
-       ret = kstrtoint(kobj->name, 16, &nid);
-       if (ret < 0)
-               return ret;
-       *codecp = dev_to_hdac_dev(dev);
-       return nid;
-}
-
-static ssize_t widget_attr_show(struct kobject *kobj, struct attribute *attr,
-                               char *buf)
-{
-       struct widget_attribute *wid_attr =
-               container_of(attr, struct widget_attribute, attr);
-       struct hdac_device *codec;
-       int nid;
-
-       if (!wid_attr->show)
-               return -EIO;
-       nid = get_codec_nid(kobj, &codec);
-       if (nid < 0)
-               return nid;
-       return wid_attr->show(codec, nid, wid_attr, buf);
-}
-
-static ssize_t widget_attr_store(struct kobject *kobj, struct attribute *attr,
-                                const char *buf, size_t count)
-{
-       struct widget_attribute *wid_attr =
-               container_of(attr, struct widget_attribute, attr);
-       struct hdac_device *codec;
-       int nid;
-
-       if (!wid_attr->store)
-               return -EIO;
-       nid = get_codec_nid(kobj, &codec);
-       if (nid < 0)
-               return nid;
-       return wid_attr->store(codec, nid, wid_attr, buf, count);
-}
-
-static const struct sysfs_ops widget_sysfs_ops = {
-       .show   = widget_attr_show,
-       .store  = widget_attr_store,
-};
-
-static void widget_release(struct kobject *kobj)
-{
-       kfree(kobj);
-}
-
-static const struct kobj_type widget_ktype = {
-       .release        = widget_release,
-       .sysfs_ops      = &widget_sysfs_ops,
-};
-
-#define WIDGET_ATTR_RO(_name) \
-       struct widget_attribute wid_attr_##_name = __ATTR_RO(_name)
-#define WIDGET_ATTR_RW(_name) \
-       struct widget_attribute wid_attr_##_name = __ATTR_RW(_name)
-
-static ssize_t caps_show(struct hdac_device *codec, hda_nid_t nid,
-                       struct widget_attribute *attr, char *buf)
-{
-       return sysfs_emit(buf, "0x%08x\n", snd_hdac_get_wcaps(codec, nid));
-}
-
-static ssize_t pin_caps_show(struct hdac_device *codec, hda_nid_t nid,
-                            struct widget_attribute *attr, char *buf)
-{
-       if (snd_hdac_get_wcaps_type(snd_hdac_get_wcaps(codec, nid)) != AC_WID_PIN)
-               return 0;
-       return sysfs_emit(buf, "0x%08x\n",
-                         snd_hdac_read_parm(codec, nid, AC_PAR_PIN_CAP));
-}
-
-static ssize_t pin_cfg_show(struct hdac_device *codec, hda_nid_t nid,
-                           struct widget_attribute *attr, char *buf)
-{
-       unsigned int val;
-
-       if (snd_hdac_get_wcaps_type(snd_hdac_get_wcaps(codec, nid)) != AC_WID_PIN)
-               return 0;
-       if (snd_hdac_read(codec, nid, AC_VERB_GET_CONFIG_DEFAULT, 0, &val))
-               return 0;
-       return sysfs_emit(buf, "0x%08x\n", val);
-}
-
-static bool has_pcm_cap(struct hdac_device *codec, hda_nid_t nid)
-{
-       if (nid == codec->afg || nid == codec->mfg)
-               return true;
-       switch (snd_hdac_get_wcaps_type(snd_hdac_get_wcaps(codec, nid))) {
-       case AC_WID_AUD_OUT:
-       case AC_WID_AUD_IN:
-               return true;
-       default:
-               return false;
-       }
-}
-
-static ssize_t pcm_caps_show(struct hdac_device *codec, hda_nid_t nid,
-                            struct widget_attribute *attr, char *buf)
-{
-       if (!has_pcm_cap(codec, nid))
-               return 0;
-       return sysfs_emit(buf, "0x%08x\n",
-                         snd_hdac_read_parm(codec, nid, AC_PAR_PCM));
-}
-
-static ssize_t pcm_formats_show(struct hdac_device *codec, hda_nid_t nid,
-                               struct widget_attribute *attr, char *buf)
-{
-       if (!has_pcm_cap(codec, nid))
-               return 0;
-       return sysfs_emit(buf, "0x%08x\n",
-                         snd_hdac_read_parm(codec, nid, AC_PAR_STREAM));
-}
-
-static ssize_t amp_in_caps_show(struct hdac_device *codec, hda_nid_t nid,
-                               struct widget_attribute *attr, char *buf)
-{
-       if (nid != codec->afg && !(snd_hdac_get_wcaps(codec, nid) & AC_WCAP_IN_AMP))
-               return 0;
-       return sysfs_emit(buf, "0x%08x\n",
-                         snd_hdac_read_parm(codec, nid, AC_PAR_AMP_IN_CAP));
-}
-
-static ssize_t amp_out_caps_show(struct hdac_device *codec, hda_nid_t nid,
-                                struct widget_attribute *attr, char *buf)
-{
-       if (nid != codec->afg && !(snd_hdac_get_wcaps(codec, nid) & AC_WCAP_OUT_AMP))
-               return 0;
-       return sysfs_emit(buf, "0x%08x\n",
-                         snd_hdac_read_parm(codec, nid, AC_PAR_AMP_OUT_CAP));
-}
-
-static ssize_t power_caps_show(struct hdac_device *codec, hda_nid_t nid,
-                              struct widget_attribute *attr, char *buf)
-{
-       if (nid != codec->afg && !(snd_hdac_get_wcaps(codec, nid) & AC_WCAP_POWER))
-               return 0;
-       return sysfs_emit(buf, "0x%08x\n",
-                         snd_hdac_read_parm(codec, nid, AC_PAR_POWER_STATE));
-}
-
-static ssize_t gpio_caps_show(struct hdac_device *codec, hda_nid_t nid,
-                             struct widget_attribute *attr, char *buf)
-{
-       return sysfs_emit(buf, "0x%08x\n",
-                         snd_hdac_read_parm(codec, nid, AC_PAR_GPIO_CAP));
-}
-
-static ssize_t connections_show(struct hdac_device *codec, hda_nid_t nid,
-                               struct widget_attribute *attr, char *buf)
-{
-       hda_nid_t list[32];
-       int i, nconns;
-       ssize_t ret = 0;
-
-       nconns = snd_hdac_get_connections(codec, nid, list, ARRAY_SIZE(list));
-       if (nconns <= 0)
-               return nconns;
-       for (i = 0; i < nconns; i++)
-               ret += sysfs_emit_at(buf,  ret, "%s0x%02x", i ? " " : "", list[i]);
-       ret += sysfs_emit_at(buf, ret, "\n");
-       return ret;
-}
-
-static WIDGET_ATTR_RO(caps);
-static WIDGET_ATTR_RO(pin_caps);
-static WIDGET_ATTR_RO(pin_cfg);
-static WIDGET_ATTR_RO(pcm_caps);
-static WIDGET_ATTR_RO(pcm_formats);
-static WIDGET_ATTR_RO(amp_in_caps);
-static WIDGET_ATTR_RO(amp_out_caps);
-static WIDGET_ATTR_RO(power_caps);
-static WIDGET_ATTR_RO(gpio_caps);
-static WIDGET_ATTR_RO(connections);
-
-static struct attribute *widget_node_attrs[] = {
-       &wid_attr_caps.attr,
-       &wid_attr_pin_caps.attr,
-       &wid_attr_pin_cfg.attr,
-       &wid_attr_pcm_caps.attr,
-       &wid_attr_pcm_formats.attr,
-       &wid_attr_amp_in_caps.attr,
-       &wid_attr_amp_out_caps.attr,
-       &wid_attr_power_caps.attr,
-       &wid_attr_connections.attr,
-       NULL,
-};
-
-static struct attribute *widget_afg_attrs[] = {
-       &wid_attr_pcm_caps.attr,
-       &wid_attr_pcm_formats.attr,
-       &wid_attr_amp_in_caps.attr,
-       &wid_attr_amp_out_caps.attr,
-       &wid_attr_power_caps.attr,
-       &wid_attr_gpio_caps.attr,
-       NULL,
-};
-
-static const struct attribute_group widget_node_group = {
-       .attrs = widget_node_attrs,
-};
-
-static const struct attribute_group widget_afg_group = {
-       .attrs = widget_afg_attrs,
-};
-
-static void free_widget_node(struct kobject *kobj,
-                            const struct attribute_group *group)
-{
-       if (kobj) {
-               sysfs_remove_group(kobj, group);
-               kobject_put(kobj);
-       }
-}
-
-static void widget_tree_free(struct hdac_device *codec)
-{
-       struct hdac_widget_tree *tree = codec->widgets;
-       struct kobject **p;
-
-       if (!tree)
-               return;
-       free_widget_node(tree->afg, &widget_afg_group);
-       if (tree->nodes) {
-               for (p = tree->nodes; *p; p++)
-                       free_widget_node(*p, &widget_node_group);
-               kfree(tree->nodes);
-       }
-       kobject_put(tree->root);
-       kfree(tree);
-       codec->widgets = NULL;
-}
-
-static int add_widget_node(struct kobject *parent, hda_nid_t nid,
-                          const struct attribute_group *group,
-                          struct kobject **res)
-{
-       struct kobject *kobj = kzalloc(sizeof(*kobj), GFP_KERNEL);
-       int err;
-
-       if (!kobj)
-               return -ENOMEM;
-       kobject_init(kobj, &widget_ktype);
-       err = kobject_add(kobj, parent, "%02x", nid);
-       if (err < 0) {
-               kobject_put(kobj);
-               return err;
-       }
-       err = sysfs_create_group(kobj, group);
-       if (err < 0) {
-               kobject_put(kobj);
-               return err;
-       }
-
-       *res = kobj;
-       return 0;
-}
-
-static int widget_tree_create(struct hdac_device *codec)
-{
-       struct hdac_widget_tree *tree;
-       int i, err;
-       hda_nid_t nid;
-
-       tree = codec->widgets = kzalloc(sizeof(*tree), GFP_KERNEL);
-       if (!tree)
-               return -ENOMEM;
-
-       tree->root = kobject_create_and_add("widgets", &codec->dev.kobj);
-       if (!tree->root)
-               return -ENOMEM;
-
-       tree->nodes = kcalloc(codec->num_nodes + 1, sizeof(*tree->nodes),
-                             GFP_KERNEL);
-       if (!tree->nodes)
-               return -ENOMEM;
-
-       for (i = 0, nid = codec->start_nid; i < codec->num_nodes; i++, nid++) {
-               err = add_widget_node(tree->root, nid, &widget_node_group,
-                                     &tree->nodes[i]);
-               if (err < 0)
-                       return err;
-       }
-
-       if (codec->afg) {
-               err = add_widget_node(tree->root, codec->afg,
-                                     &widget_afg_group, &tree->afg);
-               if (err < 0)
-                       return err;
-       }
-
-       kobject_uevent(tree->root, KOBJ_CHANGE);
-       return 0;
-}
-
-/* call with codec->widget_lock held */
-int hda_widget_sysfs_init(struct hdac_device *codec)
-{
-       int err;
-
-       if (codec->widgets)
-               return 0; /* already created */
-
-       err = widget_tree_create(codec);
-       if (err < 0) {
-               widget_tree_free(codec);
-               return err;
-       }
-
-       return 0;
-}
-
-/* call with codec->widget_lock held */
-void hda_widget_sysfs_exit(struct hdac_device *codec)
-{
-       widget_tree_free(codec);
-}
-
-/* call with codec->widget_lock held */
-int hda_widget_sysfs_reinit(struct hdac_device *codec,
-                           hda_nid_t start_nid, int num_nodes)
-{
-       struct hdac_widget_tree *tree;
-       hda_nid_t end_nid = start_nid + num_nodes;
-       hda_nid_t nid;
-       int i;
-
-       if (!codec->widgets)
-               return 0;
-
-       tree = kmemdup(codec->widgets, sizeof(*tree), GFP_KERNEL);
-       if (!tree)
-               return -ENOMEM;
-
-       tree->nodes = kcalloc(num_nodes + 1, sizeof(*tree->nodes), GFP_KERNEL);
-       if (!tree->nodes) {
-               kfree(tree);
-               return -ENOMEM;
-       }
-
-       /* prune non-existing nodes */
-       for (i = 0, nid = codec->start_nid; i < codec->num_nodes; i++, nid++) {
-               if (nid < start_nid || nid >= end_nid)
-                       free_widget_node(codec->widgets->nodes[i],
-                                        &widget_node_group);
-       }
-
-       /* add new nodes */
-       for (i = 0, nid = start_nid; i < num_nodes; i++, nid++) {
-               if (nid < codec->start_nid || nid >= codec->end_nid)
-                       add_widget_node(tree->root, nid, &widget_node_group,
-                                       &tree->nodes[i]);
-               else
-                       tree->nodes[i] =
-                               codec->widgets->nodes[nid - codec->start_nid];
-       }
-
-       /* replace with the new tree */
-       kfree(codec->widgets->nodes);
-       kfree(codec->widgets);
-       codec->widgets = tree;
-
-       kobject_uevent(tree->root, KOBJ_CHANGE);
-       return 0;
-}
diff --git a/sound/hda/hdmi_chmap.c b/sound/hda/hdmi_chmap.c
deleted file mode 100644 (file)
index 7b27604..0000000
+++ /dev/null
@@ -1,868 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0-only
-/*
- * HDMI Channel map support helpers
- */
-
-#include <linux/module.h>
-#include <sound/control.h>
-#include <sound/tlv.h>
-#include <sound/hda_chmap.h>
-
-/*
- * CEA speaker placement:
- *
- *        FLH       FCH        FRH
- *  FLW    FL  FLC   FC   FRC   FR   FRW
- *
- *                                  LFE
- *                     TC
- *
- *          RL  RLC   RC   RRC   RR
- *
- * The Left/Right Surround channel _notions_ LS/RS in SMPTE 320M corresponds to
- * CEA RL/RR; The SMPTE channel _assignment_ C/LFE is swapped to CEA LFE/FC.
- */
-enum cea_speaker_placement {
-       FL  = (1 <<  0),        /* Front Left           */
-       FC  = (1 <<  1),        /* Front Center         */
-       FR  = (1 <<  2),        /* Front Right          */
-       FLC = (1 <<  3),        /* Front Left Center    */
-       FRC = (1 <<  4),        /* Front Right Center   */
-       RL  = (1 <<  5),        /* Rear Left            */
-       RC  = (1 <<  6),        /* Rear Center          */
-       RR  = (1 <<  7),        /* Rear Right           */
-       RLC = (1 <<  8),        /* Rear Left Center     */
-       RRC = (1 <<  9),        /* Rear Right Center    */
-       LFE = (1 << 10),        /* Low Frequency Effect */
-       FLW = (1 << 11),        /* Front Left Wide      */
-       FRW = (1 << 12),        /* Front Right Wide     */
-       FLH = (1 << 13),        /* Front Left High      */
-       FCH = (1 << 14),        /* Front Center High    */
-       FRH = (1 << 15),        /* Front Right High     */
-       TC  = (1 << 16),        /* Top Center           */
-};
-
-static const char * const cea_speaker_allocation_names[] = {
-       /*  0 */ "FL/FR",
-       /*  1 */ "LFE",
-       /*  2 */ "FC",
-       /*  3 */ "RL/RR",
-       /*  4 */ "RC",
-       /*  5 */ "FLC/FRC",
-       /*  6 */ "RLC/RRC",
-       /*  7 */ "FLW/FRW",
-       /*  8 */ "FLH/FRH",
-       /*  9 */ "TC",
-       /* 10 */ "FCH",
-};
-
-/*
- * ELD SA bits in the CEA Speaker Allocation data block
- */
-static const int eld_speaker_allocation_bits[] = {
-       [0] = FL | FR,
-       [1] = LFE,
-       [2] = FC,
-       [3] = RL | RR,
-       [4] = RC,
-       [5] = FLC | FRC,
-       [6] = RLC | RRC,
-       /* the following are not defined in ELD yet */
-       [7] = FLW | FRW,
-       [8] = FLH | FRH,
-       [9] = TC,
-       [10] = FCH,
-};
-
-/*
- * ALSA sequence is:
- *
- *       surround40   surround41   surround50   surround51   surround71
- * ch0   front left   =            =            =            =
- * ch1   front right  =            =            =            =
- * ch2   rear left    =            =            =            =
- * ch3   rear right   =            =            =            =
- * ch4                LFE          center       center       center
- * ch5                                          LFE          LFE
- * ch6                                                       side left
- * ch7                                                       side right
- *
- * surround71 = {FL, FR, RLC, RRC, FC, LFE, RL, RR}
- */
-static int hdmi_channel_mapping[0x32][8] = {
-       /* stereo */
-       [0x00] = { 0x00, 0x11, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7 },
-       /* 2.1 */
-       [0x01] = { 0x00, 0x11, 0x22, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7 },
-       /* Dolby Surround */
-       [0x02] = { 0x00, 0x11, 0x23, 0xf2, 0xf4, 0xf5, 0xf6, 0xf7 },
-       /* surround40 */
-       [0x08] = { 0x00, 0x11, 0x24, 0x35, 0xf3, 0xf2, 0xf6, 0xf7 },
-       /* 4ch */
-       [0x03] = { 0x00, 0x11, 0x23, 0x32, 0x44, 0xf5, 0xf6, 0xf7 },
-       /* surround41 */
-       [0x09] = { 0x00, 0x11, 0x24, 0x35, 0x42, 0xf3, 0xf6, 0xf7 },
-       /* surround50 */
-       [0x0a] = { 0x00, 0x11, 0x24, 0x35, 0x43, 0xf2, 0xf6, 0xf7 },
-       /* surround51 */
-       [0x0b] = { 0x00, 0x11, 0x24, 0x35, 0x43, 0x52, 0xf6, 0xf7 },
-       /* 7.1 */
-       [0x13] = { 0x00, 0x11, 0x26, 0x37, 0x43, 0x52, 0x64, 0x75 },
-};
-
-/*
- * This is an ordered list!
- *
- * The preceding ones have better chances to be selected by
- * hdmi_channel_allocation().
- */
-static struct hdac_cea_channel_speaker_allocation channel_allocations[] = {
-/*                       channel:   7     6    5    4    3     2    1    0  */
-{ .ca_index = 0x00,  .speakers = {   0,    0,   0,   0,   0,    0,  FR,  FL } },
-                                /* 2.1 */
-{ .ca_index = 0x01,  .speakers = {   0,    0,   0,   0,   0,  LFE,  FR,  FL } },
-                                /* Dolby Surround */
-{ .ca_index = 0x02,  .speakers = {   0,    0,   0,   0,  FC,    0,  FR,  FL } },
-                                /* surround40 */
-{ .ca_index = 0x08,  .speakers = {   0,    0,  RR,  RL,   0,    0,  FR,  FL } },
-                                /* surround41 */
-{ .ca_index = 0x09,  .speakers = {   0,    0,  RR,  RL,   0,  LFE,  FR,  FL } },
-                                /* surround50 */
-{ .ca_index = 0x0a,  .speakers = {   0,    0,  RR,  RL,  FC,    0,  FR,  FL } },
-                                /* surround51 */
-{ .ca_index = 0x0b,  .speakers = {   0,    0,  RR,  RL,  FC,  LFE,  FR,  FL } },
-                                /* 6.1 */
-{ .ca_index = 0x0f,  .speakers = {   0,   RC,  RR,  RL,  FC,  LFE,  FR,  FL } },
-                                /* surround71 */
-{ .ca_index = 0x13,  .speakers = { RRC,  RLC,  RR,  RL,  FC,  LFE,  FR,  FL } },
-
-{ .ca_index = 0x03,  .speakers = {   0,    0,   0,   0,  FC,  LFE,  FR,  FL } },
-{ .ca_index = 0x04,  .speakers = {   0,    0,   0,  RC,   0,    0,  FR,  FL } },
-{ .ca_index = 0x05,  .speakers = {   0,    0,   0,  RC,   0,  LFE,  FR,  FL } },
-{ .ca_index = 0x06,  .speakers = {   0,    0,   0,  RC,  FC,    0,  FR,  FL } },
-{ .ca_index = 0x07,  .speakers = {   0,    0,   0,  RC,  FC,  LFE,  FR,  FL } },
-{ .ca_index = 0x0c,  .speakers = {   0,   RC,  RR,  RL,   0,    0,  FR,  FL } },
-{ .ca_index = 0x0d,  .speakers = {   0,   RC,  RR,  RL,   0,  LFE,  FR,  FL } },
-{ .ca_index = 0x0e,  .speakers = {   0,   RC,  RR,  RL,  FC,    0,  FR,  FL } },
-{ .ca_index = 0x10,  .speakers = { RRC,  RLC,  RR,  RL,   0,    0,  FR,  FL } },
-{ .ca_index = 0x11,  .speakers = { RRC,  RLC,  RR,  RL,   0,  LFE,  FR,  FL } },
-{ .ca_index = 0x12,  .speakers = { RRC,  RLC,  RR,  RL,  FC,    0,  FR,  FL } },
-{ .ca_index = 0x14,  .speakers = { FRC,  FLC,   0,   0,   0,    0,  FR,  FL } },
-{ .ca_index = 0x15,  .speakers = { FRC,  FLC,   0,   0,   0,  LFE,  FR,  FL } },
-{ .ca_index = 0x16,  .speakers = { FRC,  FLC,   0,   0,  FC,    0,  FR,  FL } },
-{ .ca_index = 0x17,  .speakers = { FRC,  FLC,   0,   0,  FC,  LFE,  FR,  FL } },
-{ .ca_index = 0x18,  .speakers = { FRC,  FLC,   0,  RC,   0,    0,  FR,  FL } },
-{ .ca_index = 0x19,  .speakers = { FRC,  FLC,   0,  RC,   0,  LFE,  FR,  FL } },
-{ .ca_index = 0x1a,  .speakers = { FRC,  FLC,   0,  RC,  FC,    0,  FR,  FL } },
-{ .ca_index = 0x1b,  .speakers = { FRC,  FLC,   0,  RC,  FC,  LFE,  FR,  FL } },
-{ .ca_index = 0x1c,  .speakers = { FRC,  FLC,  RR,  RL,   0,    0,  FR,  FL } },
-{ .ca_index = 0x1d,  .speakers = { FRC,  FLC,  RR,  RL,   0,  LFE,  FR,  FL } },
-{ .ca_index = 0x1e,  .speakers = { FRC,  FLC,  RR,  RL,  FC,    0,  FR,  FL } },
-{ .ca_index = 0x1f,  .speakers = { FRC,  FLC,  RR,  RL,  FC,  LFE,  FR,  FL } },
-{ .ca_index = 0x20,  .speakers = {   0,  FCH,  RR,  RL,  FC,    0,  FR,  FL } },
-{ .ca_index = 0x21,  .speakers = {   0,  FCH,  RR,  RL,  FC,  LFE,  FR,  FL } },
-{ .ca_index = 0x22,  .speakers = {  TC,    0,  RR,  RL,  FC,    0,  FR,  FL } },
-{ .ca_index = 0x23,  .speakers = {  TC,    0,  RR,  RL,  FC,  LFE,  FR,  FL } },
-{ .ca_index = 0x24,  .speakers = { FRH,  FLH,  RR,  RL,   0,    0,  FR,  FL } },
-{ .ca_index = 0x25,  .speakers = { FRH,  FLH,  RR,  RL,   0,  LFE,  FR,  FL } },
-{ .ca_index = 0x26,  .speakers = { FRW,  FLW,  RR,  RL,   0,    0,  FR,  FL } },
-{ .ca_index = 0x27,  .speakers = { FRW,  FLW,  RR,  RL,   0,  LFE,  FR,  FL } },
-{ .ca_index = 0x28,  .speakers = {  TC,   RC,  RR,  RL,  FC,    0,  FR,  FL } },
-{ .ca_index = 0x29,  .speakers = {  TC,   RC,  RR,  RL,  FC,  LFE,  FR,  FL } },
-{ .ca_index = 0x2a,  .speakers = { FCH,   RC,  RR,  RL,  FC,    0,  FR,  FL } },
-{ .ca_index = 0x2b,  .speakers = { FCH,   RC,  RR,  RL,  FC,  LFE,  FR,  FL } },
-{ .ca_index = 0x2c,  .speakers = {  TC,  FCH,  RR,  RL,  FC,    0,  FR,  FL } },
-{ .ca_index = 0x2d,  .speakers = {  TC,  FCH,  RR,  RL,  FC,  LFE,  FR,  FL } },
-{ .ca_index = 0x2e,  .speakers = { FRH,  FLH,  RR,  RL,  FC,    0,  FR,  FL } },
-{ .ca_index = 0x2f,  .speakers = { FRH,  FLH,  RR,  RL,  FC,  LFE,  FR,  FL } },
-{ .ca_index = 0x30,  .speakers = { FRW,  FLW,  RR,  RL,  FC,    0,  FR,  FL } },
-{ .ca_index = 0x31,  .speakers = { FRW,  FLW,  RR,  RL,  FC,  LFE,  FR,  FL } },
-};
-
-static int hdmi_pin_set_slot_channel(struct hdac_device *codec,
-               hda_nid_t pin_nid, int asp_slot, int channel)
-{
-       return snd_hdac_codec_write(codec, pin_nid, 0,
-                               AC_VERB_SET_HDMI_CHAN_SLOT,
-                               (channel << 4) | asp_slot);
-}
-
-static int hdmi_pin_get_slot_channel(struct hdac_device *codec,
-                       hda_nid_t pin_nid, int asp_slot)
-{
-       return (snd_hdac_codec_read(codec, pin_nid, 0,
-                                  AC_VERB_GET_HDMI_CHAN_SLOT,
-                                  asp_slot) & 0xf0) >> 4;
-}
-
-static int hdmi_get_channel_count(struct hdac_device *codec, hda_nid_t cvt_nid)
-{
-       return 1 + snd_hdac_codec_read(codec, cvt_nid, 0,
-                                       AC_VERB_GET_CVT_CHAN_COUNT, 0);
-}
-
-static void hdmi_set_channel_count(struct hdac_device *codec,
-                                  hda_nid_t cvt_nid, int chs)
-{
-       if (chs != hdmi_get_channel_count(codec, cvt_nid))
-               snd_hdac_codec_write(codec, cvt_nid, 0,
-                                   AC_VERB_SET_CVT_CHAN_COUNT, chs - 1);
-}
-
-/*
- * Channel mapping routines
- */
-
-/*
- * Compute derived values in channel_allocations[].
- */
-static void init_channel_allocations(void)
-{
-       int i, j;
-       struct hdac_cea_channel_speaker_allocation *p;
-
-       for (i = 0; i < ARRAY_SIZE(channel_allocations); i++) {
-               p = channel_allocations + i;
-               p->channels = 0;
-               p->spk_mask = 0;
-               for (j = 0; j < ARRAY_SIZE(p->speakers); j++)
-                       if (p->speakers[j]) {
-                               p->channels++;
-                               p->spk_mask |= p->speakers[j];
-                       }
-       }
-}
-
-static int get_channel_allocation_order(int ca)
-{
-       int i;
-
-       for (i = 0; i < ARRAY_SIZE(channel_allocations); i++) {
-               if (channel_allocations[i].ca_index == ca)
-                       break;
-       }
-       return i;
-}
-
-void snd_hdac_print_channel_allocation(int spk_alloc, char *buf, int buflen)
-{
-       int i, j;
-
-       for (i = 0, j = 0; i < ARRAY_SIZE(cea_speaker_allocation_names); i++) {
-               if (spk_alloc & (1 << i))
-                       j += scnprintf(buf + j, buflen - j,  " %s",
-                                       cea_speaker_allocation_names[i]);
-       }
-       buf[j] = '\0';  /* necessary when j == 0 */
-}
-EXPORT_SYMBOL_GPL(snd_hdac_print_channel_allocation);
-
-/*
- * The transformation takes two steps:
- *
- *     eld->spk_alloc => (eld_speaker_allocation_bits[]) => spk_mask
- *           spk_mask => (channel_allocations[])         => ai->CA
- *
- * TODO: it could select the wrong CA from multiple candidates.
-*/
-static int hdmi_channel_allocation_spk_alloc_blk(struct hdac_device *codec,
-                                  int spk_alloc, int channels)
-{
-       int i;
-       int ca = 0;
-       int spk_mask = 0;
-       char buf[SND_PRINT_CHANNEL_ALLOCATION_ADVISED_BUFSIZE];
-
-       /*
-        * CA defaults to 0 for basic stereo audio
-        */
-       if (channels <= 2)
-               return 0;
-
-       /*
-        * expand ELD's speaker allocation mask
-        *
-        * ELD tells the speaker mask in a compact(paired) form,
-        * expand ELD's notions to match the ones used by Audio InfoFrame.
-        */
-       for (i = 0; i < ARRAY_SIZE(eld_speaker_allocation_bits); i++) {
-               if (spk_alloc & (1 << i))
-                       spk_mask |= eld_speaker_allocation_bits[i];
-       }
-
-       /* search for the first working match in the CA table */
-       for (i = 0; i < ARRAY_SIZE(channel_allocations); i++) {
-               if (channels == channel_allocations[i].channels &&
-                   (spk_mask & channel_allocations[i].spk_mask) ==
-                               channel_allocations[i].spk_mask) {
-                       ca = channel_allocations[i].ca_index;
-                       break;
-               }
-       }
-
-       if (!ca) {
-               /*
-                * if there was no match, select the regular ALSA channel
-                * allocation with the matching number of channels
-                */
-               for (i = 0; i < ARRAY_SIZE(channel_allocations); i++) {
-                       if (channels == channel_allocations[i].channels) {
-                               ca = channel_allocations[i].ca_index;
-                               break;
-                       }
-               }
-       }
-
-       snd_hdac_print_channel_allocation(spk_alloc, buf, sizeof(buf));
-       dev_dbg(&codec->dev, "HDMI: select CA 0x%x for %d-channel allocation: %s\n",
-                   ca, channels, buf);
-
-       return ca;
-}
-
-static void hdmi_debug_channel_mapping(struct hdac_chmap *chmap,
-                                      hda_nid_t pin_nid)
-{
-#ifdef CONFIG_SND_DEBUG_VERBOSE
-       int i;
-       int channel;
-
-       for (i = 0; i < 8; i++) {
-               channel = chmap->ops.pin_get_slot_channel(
-                               chmap->hdac, pin_nid, i);
-               dev_dbg(&chmap->hdac->dev, "HDMI: ASP channel %d => slot %d\n",
-                                               channel, i);
-       }
-#endif
-}
-
-static void hdmi_std_setup_channel_mapping(struct hdac_chmap *chmap,
-                                      hda_nid_t pin_nid,
-                                      bool non_pcm,
-                                      int ca)
-{
-       struct hdac_cea_channel_speaker_allocation *ch_alloc;
-       int i;
-       int err;
-       int order;
-       int non_pcm_mapping[8];
-
-       order = get_channel_allocation_order(ca);
-       ch_alloc = &channel_allocations[order];
-
-       if (hdmi_channel_mapping[ca][1] == 0) {
-               int hdmi_slot = 0;
-               /* fill actual channel mappings in ALSA channel (i) order */
-               for (i = 0; i < ch_alloc->channels; i++) {
-                       while (!WARN_ON(hdmi_slot >= 8) &&
-                              !ch_alloc->speakers[7 - hdmi_slot])
-                               hdmi_slot++; /* skip zero slots */
-
-                       hdmi_channel_mapping[ca][i] = (i << 4) | hdmi_slot++;
-               }
-               /* fill the rest of the slots with ALSA channel 0xf */
-               for (hdmi_slot = 0; hdmi_slot < 8; hdmi_slot++)
-                       if (!ch_alloc->speakers[7 - hdmi_slot])
-                               hdmi_channel_mapping[ca][i++] = (0xf << 4) | hdmi_slot;
-       }
-
-       if (non_pcm) {
-               for (i = 0; i < ch_alloc->channels; i++)
-                       non_pcm_mapping[i] = (i << 4) | i;
-               for (; i < 8; i++)
-                       non_pcm_mapping[i] = (0xf << 4) | i;
-       }
-
-       for (i = 0; i < 8; i++) {
-               int slotsetup = non_pcm ? non_pcm_mapping[i] : hdmi_channel_mapping[ca][i];
-               int hdmi_slot = slotsetup & 0x0f;
-               int channel = (slotsetup & 0xf0) >> 4;
-
-               err = chmap->ops.pin_set_slot_channel(chmap->hdac,
-                               pin_nid, hdmi_slot, channel);
-               if (err) {
-                       dev_dbg(&chmap->hdac->dev, "HDMI: channel mapping failed\n");
-                       break;
-               }
-       }
-}
-
-struct channel_map_table {
-       unsigned char map;              /* ALSA API channel map position */
-       int spk_mask;                   /* speaker position bit mask */
-};
-
-static struct channel_map_table map_tables[] = {
-       { SNDRV_CHMAP_FL,       FL },
-       { SNDRV_CHMAP_FR,       FR },
-       { SNDRV_CHMAP_RL,       RL },
-       { SNDRV_CHMAP_RR,       RR },
-       { SNDRV_CHMAP_LFE,      LFE },
-       { SNDRV_CHMAP_FC,       FC },
-       { SNDRV_CHMAP_RLC,      RLC },
-       { SNDRV_CHMAP_RRC,      RRC },
-       { SNDRV_CHMAP_RC,       RC },
-       { SNDRV_CHMAP_FLC,      FLC },
-       { SNDRV_CHMAP_FRC,      FRC },
-       { SNDRV_CHMAP_TFL,      FLH },
-       { SNDRV_CHMAP_TFR,      FRH },
-       { SNDRV_CHMAP_FLW,      FLW },
-       { SNDRV_CHMAP_FRW,      FRW },
-       { SNDRV_CHMAP_TC,       TC },
-       { SNDRV_CHMAP_TFC,      FCH },
-       {} /* terminator */
-};
-
-/* from ALSA API channel position to speaker bit mask */
-int snd_hdac_chmap_to_spk_mask(unsigned char c)
-{
-       struct channel_map_table *t = map_tables;
-
-       for (; t->map; t++) {
-               if (t->map == c)
-                       return t->spk_mask;
-       }
-       return 0;
-}
-EXPORT_SYMBOL_GPL(snd_hdac_chmap_to_spk_mask);
-
-/* from ALSA API channel position to CEA slot */
-static int to_cea_slot(int ordered_ca, unsigned char pos)
-{
-       int mask = snd_hdac_chmap_to_spk_mask(pos);
-       int i;
-
-       /* Add sanity check to pass klockwork check.
-        * This should never happen.
-        */
-       if (ordered_ca >= ARRAY_SIZE(channel_allocations))
-               return -1;
-
-       if (mask) {
-               for (i = 0; i < 8; i++) {
-                       if (channel_allocations[ordered_ca].speakers[7 - i] == mask)
-                               return i;
-               }
-       }
-
-       return -1;
-}
-
-/* from speaker bit mask to ALSA API channel position */
-int snd_hdac_spk_to_chmap(int spk)
-{
-       struct channel_map_table *t = map_tables;
-
-       for (; t->map; t++) {
-               if (t->spk_mask == spk)
-                       return t->map;
-       }
-       return 0;
-}
-EXPORT_SYMBOL_GPL(snd_hdac_spk_to_chmap);
-
-/* from CEA slot to ALSA API channel position */
-static int from_cea_slot(int ordered_ca, unsigned char slot)
-{
-       int mask;
-
-       /* Add sanity check to pass klockwork check.
-        * This should never happen.
-        */
-       if (slot >= 8)
-               return 0;
-
-       mask = channel_allocations[ordered_ca].speakers[7 - slot];
-
-       return snd_hdac_spk_to_chmap(mask);
-}
-
-/* get the CA index corresponding to the given ALSA API channel map */
-static int hdmi_manual_channel_allocation(int chs, unsigned char *map)
-{
-       int i, spks = 0, spk_mask = 0;
-
-       for (i = 0; i < chs; i++) {
-               int mask = snd_hdac_chmap_to_spk_mask(map[i]);
-
-               if (mask) {
-                       spk_mask |= mask;
-                       spks++;
-               }
-       }
-
-       for (i = 0; i < ARRAY_SIZE(channel_allocations); i++) {
-               if ((chs == channel_allocations[i].channels ||
-                    spks == channel_allocations[i].channels) &&
-                   (spk_mask & channel_allocations[i].spk_mask) ==
-                               channel_allocations[i].spk_mask)
-                       return channel_allocations[i].ca_index;
-       }
-       return -1;
-}
-
-/* set up the channel slots for the given ALSA API channel map */
-static int hdmi_manual_setup_channel_mapping(struct hdac_chmap *chmap,
-                                            hda_nid_t pin_nid,
-                                            int chs, unsigned char *map,
-                                            int ca)
-{
-       int ordered_ca = get_channel_allocation_order(ca);
-       int alsa_pos, hdmi_slot;
-       int assignments[8] = {[0 ... 7] = 0xf};
-
-       for (alsa_pos = 0; alsa_pos < chs; alsa_pos++) {
-
-               hdmi_slot = to_cea_slot(ordered_ca, map[alsa_pos]);
-
-               if (hdmi_slot < 0)
-                       continue; /* unassigned channel */
-
-               assignments[hdmi_slot] = alsa_pos;
-       }
-
-       for (hdmi_slot = 0; hdmi_slot < 8; hdmi_slot++) {
-               int err;
-
-               err = chmap->ops.pin_set_slot_channel(chmap->hdac,
-                               pin_nid, hdmi_slot, assignments[hdmi_slot]);
-               if (err)
-                       return -EINVAL;
-       }
-       return 0;
-}
-
-/* store ALSA API channel map from the current default map */
-static void hdmi_setup_fake_chmap(unsigned char *map, int ca)
-{
-       int i;
-       int ordered_ca = get_channel_allocation_order(ca);
-
-       for (i = 0; i < 8; i++) {
-               if (ordered_ca < ARRAY_SIZE(channel_allocations) &&
-                   i < channel_allocations[ordered_ca].channels)
-                       map[i] = from_cea_slot(ordered_ca, hdmi_channel_mapping[ca][i] & 0x0f);
-               else
-                       map[i] = 0;
-       }
-}
-
-void snd_hdac_setup_channel_mapping(struct hdac_chmap *chmap,
-                                      hda_nid_t pin_nid, bool non_pcm, int ca,
-                                      int channels, unsigned char *map,
-                                      bool chmap_set)
-{
-       if (!non_pcm && chmap_set) {
-               hdmi_manual_setup_channel_mapping(chmap, pin_nid,
-                                                 channels, map, ca);
-       } else {
-               hdmi_std_setup_channel_mapping(chmap, pin_nid, non_pcm, ca);
-               hdmi_setup_fake_chmap(map, ca);
-       }
-
-       hdmi_debug_channel_mapping(chmap, pin_nid);
-}
-EXPORT_SYMBOL_GPL(snd_hdac_setup_channel_mapping);
-
-int snd_hdac_get_active_channels(int ca)
-{
-       int ordered_ca = get_channel_allocation_order(ca);
-
-       /* Add sanity check to pass klockwork check.
-        * This should never happen.
-        */
-       if (ordered_ca >= ARRAY_SIZE(channel_allocations))
-               ordered_ca = 0;
-
-       return channel_allocations[ordered_ca].channels;
-}
-EXPORT_SYMBOL_GPL(snd_hdac_get_active_channels);
-
-struct hdac_cea_channel_speaker_allocation *snd_hdac_get_ch_alloc_from_ca(int ca)
-{
-       return &channel_allocations[get_channel_allocation_order(ca)];
-}
-EXPORT_SYMBOL_GPL(snd_hdac_get_ch_alloc_from_ca);
-
-int snd_hdac_channel_allocation(struct hdac_device *hdac, int spk_alloc,
-               int channels, bool chmap_set, bool non_pcm, unsigned char *map)
-{
-       int ca;
-
-       if (!non_pcm && chmap_set)
-               ca = hdmi_manual_channel_allocation(channels, map);
-       else
-               ca = hdmi_channel_allocation_spk_alloc_blk(hdac,
-                                       spk_alloc, channels);
-
-       if (ca < 0)
-               ca = 0;
-
-       return ca;
-}
-EXPORT_SYMBOL_GPL(snd_hdac_channel_allocation);
-
-/*
- * ALSA API channel-map control callbacks
- */
-static int hdmi_chmap_ctl_info(struct snd_kcontrol *kcontrol,
-                              struct snd_ctl_elem_info *uinfo)
-{
-       struct snd_pcm_chmap *info = snd_kcontrol_chip(kcontrol);
-       struct hdac_chmap *chmap = info->private_data;
-
-       uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
-       uinfo->count = chmap->channels_max;
-       uinfo->value.integer.min = 0;
-       uinfo->value.integer.max = SNDRV_CHMAP_LAST;
-       return 0;
-}
-
-static int hdmi_chmap_cea_alloc_validate_get_type(struct hdac_chmap *chmap,
-               struct hdac_cea_channel_speaker_allocation *cap, int channels)
-{
-       /* If the speaker allocation matches the channel count, it is OK.*/
-       if (cap->channels != channels)
-               return -1;
-
-       /* all channels are remappable freely */
-       return SNDRV_CTL_TLVT_CHMAP_VAR;
-}
-
-static void hdmi_cea_alloc_to_tlv_chmap(struct hdac_chmap *hchmap,
-               struct hdac_cea_channel_speaker_allocation *cap,
-               unsigned int *chmap, int channels)
-{
-       int count = 0;
-       int c;
-
-       for (c = 7; c >= 0; c--) {
-               int spk = cap->speakers[c];
-
-               if (!spk)
-                       continue;
-
-               chmap[count++] = snd_hdac_spk_to_chmap(spk);
-       }
-
-       WARN_ON(count != channels);
-}
-
-static int spk_mask_from_spk_alloc(int spk_alloc)
-{
-       int i;
-       int spk_mask = eld_speaker_allocation_bits[0];
-
-       for (i = 0; i < ARRAY_SIZE(eld_speaker_allocation_bits); i++) {
-               if (spk_alloc & (1 << i))
-                       spk_mask |= eld_speaker_allocation_bits[i];
-       }
-
-       return spk_mask;
-}
-
-static int hdmi_chmap_ctl_tlv(struct snd_kcontrol *kcontrol, int op_flag,
-                             unsigned int size, unsigned int __user *tlv)
-{
-       struct snd_pcm_chmap *info = snd_kcontrol_chip(kcontrol);
-       struct hdac_chmap *chmap = info->private_data;
-       int pcm_idx = kcontrol->private_value;
-       unsigned int __user *dst;
-       int chs, count = 0;
-       unsigned long max_chs;
-       int type;
-       int spk_alloc, spk_mask;
-
-       if (size < 8)
-               return -ENOMEM;
-       if (put_user(SNDRV_CTL_TLVT_CONTAINER, tlv))
-               return -EFAULT;
-       size -= 8;
-       dst = tlv + 2;
-
-       spk_alloc = chmap->ops.get_spk_alloc(chmap->hdac, pcm_idx);
-       spk_mask = spk_mask_from_spk_alloc(spk_alloc);
-
-       max_chs = hweight_long(spk_mask);
-
-       for (chs = 2; chs <= max_chs; chs++) {
-               int i;
-               struct hdac_cea_channel_speaker_allocation *cap;
-
-               cap = channel_allocations;
-               for (i = 0; i < ARRAY_SIZE(channel_allocations); i++, cap++) {
-                       int chs_bytes = chs * 4;
-                       unsigned int tlv_chmap[8];
-
-                       if (cap->channels != chs)
-                               continue;
-
-                       if (!(cap->spk_mask == (spk_mask & cap->spk_mask)))
-                               continue;
-
-                       type = chmap->ops.chmap_cea_alloc_validate_get_type(
-                                                       chmap, cap, chs);
-                       if (type < 0)
-                               return -ENODEV;
-                       if (size < 8)
-                               return -ENOMEM;
-
-                       if (put_user(type, dst) ||
-                           put_user(chs_bytes, dst + 1))
-                               return -EFAULT;
-
-                       dst += 2;
-                       size -= 8;
-                       count += 8;
-
-                       if (size < chs_bytes)
-                               return -ENOMEM;
-
-                       size -= chs_bytes;
-                       count += chs_bytes;
-                       chmap->ops.cea_alloc_to_tlv_chmap(chmap, cap,
-                                               tlv_chmap, chs);
-
-                       if (copy_to_user(dst, tlv_chmap, chs_bytes))
-                               return -EFAULT;
-                       dst += chs;
-               }
-       }
-
-       if (put_user(count, tlv + 1))
-               return -EFAULT;
-
-       return 0;
-}
-
-static int hdmi_chmap_ctl_get(struct snd_kcontrol *kcontrol,
-                             struct snd_ctl_elem_value *ucontrol)
-{
-       struct snd_pcm_chmap *info = snd_kcontrol_chip(kcontrol);
-       struct hdac_chmap *chmap = info->private_data;
-       int pcm_idx = kcontrol->private_value;
-       unsigned char pcm_chmap[8];
-       int i;
-
-       memset(pcm_chmap, 0, sizeof(pcm_chmap));
-       chmap->ops.get_chmap(chmap->hdac, pcm_idx, pcm_chmap);
-
-       for (i = 0; i < ARRAY_SIZE(pcm_chmap); i++)
-               ucontrol->value.integer.value[i] = pcm_chmap[i];
-
-       return 0;
-}
-
-/* a simple sanity check for input values to chmap kcontrol */
-static int chmap_value_check(struct hdac_chmap *hchmap,
-                            const struct snd_ctl_elem_value *ucontrol)
-{
-       int i;
-
-       for (i = 0; i < hchmap->channels_max; i++) {
-               if (ucontrol->value.integer.value[i] < 0 ||
-                   ucontrol->value.integer.value[i] > SNDRV_CHMAP_LAST)
-                       return -EINVAL;
-       }
-       return 0;
-}
-
-static int hdmi_chmap_ctl_put(struct snd_kcontrol *kcontrol,
-                             struct snd_ctl_elem_value *ucontrol)
-{
-       struct snd_pcm_chmap *info = snd_kcontrol_chip(kcontrol);
-       struct hdac_chmap *hchmap = info->private_data;
-       int pcm_idx = kcontrol->private_value;
-       unsigned int ctl_idx;
-       struct snd_pcm_substream *substream;
-       unsigned char chmap[8], per_pin_chmap[8];
-       int i, err, ca, prepared = 0;
-
-       err = chmap_value_check(hchmap, ucontrol);
-       if (err < 0)
-               return err;
-
-       /* No monitor is connected in dyn_pcm_assign.
-        * It's invalid to setup the chmap
-        */
-       if (!hchmap->ops.is_pcm_attached(hchmap->hdac, pcm_idx))
-               return 0;
-
-       ctl_idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
-       substream = snd_pcm_chmap_substream(info, ctl_idx);
-       if (!substream || !substream->runtime)
-               return 0; /* just for avoiding error from alsactl restore */
-       switch (substream->runtime->state) {
-       case SNDRV_PCM_STATE_OPEN:
-       case SNDRV_PCM_STATE_SETUP:
-               break;
-       case SNDRV_PCM_STATE_PREPARED:
-               prepared = 1;
-               break;
-       default:
-               return -EBUSY;
-       }
-       memset(chmap, 0, sizeof(chmap));
-       for (i = 0; i < ARRAY_SIZE(chmap); i++)
-               chmap[i] = ucontrol->value.integer.value[i];
-
-       hchmap->ops.get_chmap(hchmap->hdac, pcm_idx, per_pin_chmap);
-       if (!memcmp(chmap, per_pin_chmap, sizeof(chmap)))
-               return 0;
-       ca = hdmi_manual_channel_allocation(ARRAY_SIZE(chmap), chmap);
-       if (ca < 0)
-               return -EINVAL;
-       if (hchmap->ops.chmap_validate) {
-               err = hchmap->ops.chmap_validate(hchmap, ca,
-                               ARRAY_SIZE(chmap), chmap);
-               if (err)
-                       return err;
-       }
-
-       hchmap->ops.set_chmap(hchmap->hdac, pcm_idx, chmap, prepared);
-
-       return 0;
-}
-
-static const struct hdac_chmap_ops chmap_ops = {
-       .chmap_cea_alloc_validate_get_type      = hdmi_chmap_cea_alloc_validate_get_type,
-       .cea_alloc_to_tlv_chmap                 = hdmi_cea_alloc_to_tlv_chmap,
-       .pin_get_slot_channel                   = hdmi_pin_get_slot_channel,
-       .pin_set_slot_channel                   = hdmi_pin_set_slot_channel,
-       .set_channel_count                      = hdmi_set_channel_count,
-};
-
-void snd_hdac_register_chmap_ops(struct hdac_device *hdac,
-                               struct hdac_chmap *chmap)
-{
-       chmap->ops = chmap_ops;
-       chmap->hdac = hdac;
-       init_channel_allocations();
-}
-EXPORT_SYMBOL_GPL(snd_hdac_register_chmap_ops);
-
-int snd_hdac_add_chmap_ctls(struct snd_pcm *pcm, int pcm_idx,
-                               struct hdac_chmap *hchmap)
-{
-       struct snd_pcm_chmap *chmap;
-       struct snd_kcontrol *kctl;
-       int err, i;
-
-       err = snd_pcm_add_chmap_ctls(pcm,
-                                    SNDRV_PCM_STREAM_PLAYBACK,
-                                    NULL, 0, pcm_idx, &chmap);
-       if (err < 0)
-               return err;
-       /* override handlers */
-       chmap->private_data = hchmap;
-       kctl = chmap->kctl;
-       for (i = 0; i < kctl->count; i++)
-               kctl->vd[i].access |= SNDRV_CTL_ELEM_ACCESS_WRITE;
-       kctl->info = hdmi_chmap_ctl_info;
-       kctl->get = hdmi_chmap_ctl_get;
-       kctl->put = hdmi_chmap_ctl_put;
-       kctl->tlv.c = hdmi_chmap_ctl_tlv;
-
-       return 0;
-}
-EXPORT_SYMBOL_GPL(snd_hdac_add_chmap_ctls);
diff --git a/sound/hda/intel-dsp-config.c b/sound/hda/intel-dsp-config.c
deleted file mode 100644 (file)
index 3cb1e7f..0000000
+++ /dev/null
@@ -1,821 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0
-// Copyright (c) 2019 Jaroslav Kysela <perex@perex.cz>
-
-#include <linux/acpi.h>
-#include <linux/bits.h>
-#include <linux/dmi.h>
-#include <linux/module.h>
-#include <linux/pci.h>
-#include <linux/soundwire/sdw.h>
-#include <linux/soundwire/sdw_intel.h>
-#include <sound/core.h>
-#include <sound/intel-dsp-config.h>
-#include <sound/intel-nhlt.h>
-#include <sound/soc-acpi.h>
-
-#include <acpi/nhlt.h>
-
-static int dsp_driver;
-
-module_param(dsp_driver, int, 0444);
-MODULE_PARM_DESC(dsp_driver, "Force the DSP driver for Intel DSP (0=auto, 1=legacy, 2=SST, 3=SOF, 4=AVS)");
-
-#define FLAG_SST                       BIT(0)
-#define FLAG_SOF                       BIT(1)
-#define FLAG_SST_ONLY_IF_DMIC          BIT(15)
-#define FLAG_SOF_ONLY_IF_DMIC          BIT(16)
-#define FLAG_SOF_ONLY_IF_SOUNDWIRE     BIT(17)
-
-#define FLAG_SOF_ONLY_IF_DMIC_OR_SOUNDWIRE (FLAG_SOF_ONLY_IF_DMIC | \
-                                           FLAG_SOF_ONLY_IF_SOUNDWIRE)
-
-struct config_entry {
-       u32 flags;
-       u16 device;
-       u8 acpi_hid[ACPI_ID_LEN];
-       const struct dmi_system_id *dmi_table;
-       const struct snd_soc_acpi_codecs *codec_hid;
-};
-
-static const struct snd_soc_acpi_codecs __maybe_unused essx_83x6 = {
-       .num_codecs = 3,
-       .codecs = { "ESSX8316", "ESSX8326", "ESSX8336"},
-};
-
-/*
- * configuration table
- * - the order of similar PCI ID entries is important!
- * - the first successful match will win
- */
-static const struct config_entry config_table[] = {
-/* Merrifield */
-#if IS_ENABLED(CONFIG_SND_SOC_SOF_MERRIFIELD)
-       {
-               .flags = FLAG_SOF,
-               .device = PCI_DEVICE_ID_INTEL_SST_TNG,
-       },
-#endif
-/*
- * Skylake, Kabylake, Apollolake
- * the legacy HDAudio driver is used except on Up Squared (SOF) and
- * Chromebooks (SST), as well as devices based on the ES8336 codec
- */
-#if IS_ENABLED(CONFIG_SND_SOC_INTEL_AVS)
-       {
-               .flags = FLAG_SST,
-               .device = PCI_DEVICE_ID_INTEL_HDA_SKL_LP,
-               .dmi_table = (const struct dmi_system_id []) {
-                       {
-                               .ident = "Google Chromebooks",
-                               .matches = {
-                                       DMI_MATCH(DMI_SYS_VENDOR, "Google"),
-                               }
-                       },
-                       {}
-               }
-       },
-       {
-               .flags = FLAG_SST | FLAG_SST_ONLY_IF_DMIC,
-               .device = PCI_DEVICE_ID_INTEL_HDA_SKL_LP,
-       },
-       {
-               .flags = FLAG_SST,
-               .device = PCI_DEVICE_ID_INTEL_HDA_KBL_LP,
-               .dmi_table = (const struct dmi_system_id []) {
-                       {
-                               .ident = "Google Chromebooks",
-                               .matches = {
-                                       DMI_MATCH(DMI_SYS_VENDOR, "Google"),
-                               }
-                       },
-                       {}
-               }
-       },
-       {
-               .flags = FLAG_SST | FLAG_SST_ONLY_IF_DMIC,
-               .device = PCI_DEVICE_ID_INTEL_HDA_KBL_LP,
-       },
-       {
-               .flags = FLAG_SST,
-               .device = PCI_DEVICE_ID_INTEL_HDA_APL,
-               .dmi_table = (const struct dmi_system_id []) {
-                       {
-                               .ident = "Google Chromebooks",
-                               .matches = {
-                                       DMI_MATCH(DMI_SYS_VENDOR, "Google"),
-                               }
-                       },
-                       {}
-               }
-       },
-       {
-               .flags = FLAG_SST,
-               .device = PCI_DEVICE_ID_INTEL_HDA_RPL_M,
-       },
-       {
-               .flags = FLAG_SST,
-               .device = PCI_DEVICE_ID_INTEL_HDA_FCL,
-       },
-#endif
-#if IS_ENABLED(CONFIG_SND_SOC_SOF_APOLLOLAKE)
-       {
-               .flags = FLAG_SOF,
-               .device = PCI_DEVICE_ID_INTEL_HDA_APL,
-               .dmi_table = (const struct dmi_system_id []) {
-                       {
-                               .ident = "Up Squared",
-                               .matches = {
-                                       DMI_MATCH(DMI_SYS_VENDOR, "AAEON"),
-                                       DMI_MATCH(DMI_BOARD_NAME, "UP-APL01"),
-                               }
-                       },
-                       {}
-               }
-       },
-       {
-               .flags = FLAG_SOF,
-               .device = PCI_DEVICE_ID_INTEL_HDA_APL,
-               .codec_hid =  &essx_83x6,
-       },
-#endif
-
-/*
- * Geminilake uses legacy HDAudio driver except for Google
- * Chromebooks and devices based on the ES8336 codec
- */
-/* Geminilake */
-#if IS_ENABLED(CONFIG_SND_SOC_SOF_GEMINILAKE)
-       {
-               .flags = FLAG_SOF,
-               .device = PCI_DEVICE_ID_INTEL_HDA_GML,
-               .dmi_table = (const struct dmi_system_id []) {
-                       {
-                               .ident = "Google Chromebooks",
-                               .matches = {
-                                       DMI_MATCH(DMI_SYS_VENDOR, "Google"),
-                               }
-                       },
-                       {}
-               }
-       },
-       {
-               .flags = FLAG_SOF,
-               .device = PCI_DEVICE_ID_INTEL_HDA_GML,
-               .codec_hid =  &essx_83x6,
-       },
-#endif
-
-/*
- * CoffeeLake, CannonLake, CometLake, IceLake, TigerLake, AlderLake,
- * RaptorLake use legacy HDAudio driver except for Google Chromebooks
- * and when DMICs are present. Two cases are required since Coreboot
- * does not expose NHLT tables.
- *
- * When the Chromebook quirk is not present, it's based on information
- * that no such device exists. When the quirk is present, it could be
- * either based on product information or a placeholder.
- */
-
-/* Cannonlake */
-#if IS_ENABLED(CONFIG_SND_SOC_SOF_CANNONLAKE)
-       {
-               .flags = FLAG_SOF,
-               .device = PCI_DEVICE_ID_INTEL_HDA_CNL_LP,
-               .dmi_table = (const struct dmi_system_id []) {
-                       {
-                               .ident = "Google Chromebooks",
-                               .matches = {
-                                       DMI_MATCH(DMI_SYS_VENDOR, "Google"),
-                               }
-                       },
-                       {
-                               .ident = "UP-WHL",
-                               .matches = {
-                                       DMI_MATCH(DMI_SYS_VENDOR, "AAEON"),
-                               }
-                       },
-                       {}
-               }
-       },
-       {
-               .flags = FLAG_SOF,
-               .device = PCI_DEVICE_ID_INTEL_HDA_CNL_LP,
-               .codec_hid =  &essx_83x6,
-       },
-       {
-               .flags = FLAG_SOF | FLAG_SOF_ONLY_IF_DMIC_OR_SOUNDWIRE,
-               .device = PCI_DEVICE_ID_INTEL_HDA_CNL_LP,
-       },
-#endif
-
-/* Coffelake */
-#if IS_ENABLED(CONFIG_SND_SOC_SOF_COFFEELAKE)
-       {
-               .flags = FLAG_SOF,
-               .device = PCI_DEVICE_ID_INTEL_HDA_CNL_H,
-               .dmi_table = (const struct dmi_system_id []) {
-                       {
-                               .ident = "Google Chromebooks",
-                               .matches = {
-                                       DMI_MATCH(DMI_SYS_VENDOR, "Google"),
-                               }
-                       },
-                       {}
-               }
-       },
-       {
-               .flags = FLAG_SOF | FLAG_SOF_ONLY_IF_DMIC_OR_SOUNDWIRE,
-               .device = PCI_DEVICE_ID_INTEL_HDA_CNL_H,
-       },
-#endif
-
-#if IS_ENABLED(CONFIG_SND_SOC_SOF_COMETLAKE)
-/* Cometlake-LP */
-       {
-               .flags = FLAG_SOF,
-               .device = PCI_DEVICE_ID_INTEL_HDA_CML_LP,
-               .dmi_table = (const struct dmi_system_id []) {
-                       {
-                               .ident = "Google Chromebooks",
-                               .matches = {
-                                       DMI_MATCH(DMI_SYS_VENDOR, "Google"),
-                               }
-                       },
-                       {
-                               .matches = {
-                                       DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc"),
-                                       DMI_EXACT_MATCH(DMI_PRODUCT_SKU, "09C6")
-                               },
-                       },
-                       {
-                               /* early version of SKU 09C6 */
-                               .matches = {
-                                       DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc"),
-                                       DMI_EXACT_MATCH(DMI_PRODUCT_SKU, "0983")
-                               },
-                       },
-                       {}
-               }
-       },
-       {
-               .flags = FLAG_SOF,
-               .device = PCI_DEVICE_ID_INTEL_HDA_CML_LP,
-               .codec_hid =  &essx_83x6,
-       },
-       {
-               .flags = FLAG_SOF | FLAG_SOF_ONLY_IF_DMIC_OR_SOUNDWIRE,
-               .device = PCI_DEVICE_ID_INTEL_HDA_CML_LP,
-       },
-/* Cometlake-H */
-       {
-               .flags = FLAG_SOF,
-               .device = PCI_DEVICE_ID_INTEL_HDA_CML_H,
-               .dmi_table = (const struct dmi_system_id []) {
-                       {
-                               .matches = {
-                                       DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc"),
-                                       DMI_EXACT_MATCH(DMI_PRODUCT_SKU, "098F"),
-                               },
-                       },
-                       {
-                               .matches = {
-                                       DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc"),
-                                       DMI_EXACT_MATCH(DMI_PRODUCT_SKU, "0990"),
-                               },
-                       },
-                       {}
-               }
-       },
-       {
-               .flags = FLAG_SOF,
-               .device = PCI_DEVICE_ID_INTEL_HDA_CML_H,
-               .codec_hid =  &essx_83x6,
-       },
-       {
-               .flags = FLAG_SOF | FLAG_SOF_ONLY_IF_DMIC_OR_SOUNDWIRE,
-               .device = PCI_DEVICE_ID_INTEL_HDA_CML_H,
-       },
-#endif
-
-/* Icelake */
-#if IS_ENABLED(CONFIG_SND_SOC_SOF_ICELAKE)
-       {
-               .flags = FLAG_SOF,
-               .device = PCI_DEVICE_ID_INTEL_HDA_ICL_LP,
-               .dmi_table = (const struct dmi_system_id []) {
-                       {
-                               .ident = "Google Chromebooks",
-                               .matches = {
-                                       DMI_MATCH(DMI_SYS_VENDOR, "Google"),
-                               }
-                       },
-                       {}
-               }
-       },
-       {
-               .flags = FLAG_SOF,
-               .device = PCI_DEVICE_ID_INTEL_HDA_ICL_LP,
-               .codec_hid =  &essx_83x6,
-       },
-       {
-               .flags = FLAG_SOF | FLAG_SOF_ONLY_IF_DMIC_OR_SOUNDWIRE,
-               .device = PCI_DEVICE_ID_INTEL_HDA_ICL_LP,
-       },
-#endif
-
-/* Jasper Lake */
-#if IS_ENABLED(CONFIG_SND_SOC_SOF_JASPERLAKE)
-       {
-               .flags = FLAG_SOF,
-               .device = PCI_DEVICE_ID_INTEL_HDA_JSL_N,
-               .dmi_table = (const struct dmi_system_id []) {
-                       {
-                               .ident = "Google Chromebooks",
-                               .matches = {
-                                       DMI_MATCH(DMI_SYS_VENDOR, "Google"),
-                               }
-                       },
-                       {
-                               .ident = "Google firmware",
-                               .matches = {
-                                       DMI_MATCH(DMI_BIOS_VERSION, "Google"),
-                               }
-                       },
-                       {}
-               }
-       },
-       {
-               .flags = FLAG_SOF,
-               .device = PCI_DEVICE_ID_INTEL_HDA_JSL_N,
-               .codec_hid =  &essx_83x6,
-       },
-       {
-               .flags = FLAG_SOF | FLAG_SOF_ONLY_IF_DMIC,
-               .device = PCI_DEVICE_ID_INTEL_HDA_JSL_N,
-       },
-#endif
-
-/* Tigerlake */
-#if IS_ENABLED(CONFIG_SND_SOC_SOF_TIGERLAKE)
-       {
-               .flags = FLAG_SOF,
-               .device = PCI_DEVICE_ID_INTEL_HDA_TGL_LP,
-               .dmi_table = (const struct dmi_system_id []) {
-                       {
-                               .ident = "Google Chromebooks",
-                               .matches = {
-                                       DMI_MATCH(DMI_SYS_VENDOR, "Google"),
-                               }
-                       },
-                       {
-                               .ident = "UPX-TGL",
-                               .matches = {
-                                       DMI_MATCH(DMI_SYS_VENDOR, "AAEON"),
-                               }
-                       },
-                       {}
-               }
-       },
-       {
-               .flags = FLAG_SOF,
-               .device = PCI_DEVICE_ID_INTEL_HDA_TGL_LP,
-               .codec_hid =  &essx_83x6,
-       },
-       {
-               .flags = FLAG_SOF | FLAG_SOF_ONLY_IF_DMIC_OR_SOUNDWIRE,
-               .device = PCI_DEVICE_ID_INTEL_HDA_TGL_LP,
-       },
-       {
-               .flags = FLAG_SOF | FLAG_SOF_ONLY_IF_DMIC_OR_SOUNDWIRE,
-               .device = PCI_DEVICE_ID_INTEL_HDA_TGL_H,
-       },
-#endif
-
-/* Elkhart Lake */
-#if IS_ENABLED(CONFIG_SND_SOC_SOF_ELKHARTLAKE)
-       {
-               .flags = FLAG_SOF | FLAG_SOF_ONLY_IF_DMIC,
-               .device = PCI_DEVICE_ID_INTEL_HDA_EHL_0,
-       },
-       {
-               .flags = FLAG_SOF | FLAG_SOF_ONLY_IF_DMIC,
-               .device = PCI_DEVICE_ID_INTEL_HDA_EHL_3,
-       },
-#endif
-
-/* Alder Lake / Raptor Lake */
-#if IS_ENABLED(CONFIG_SND_SOC_SOF_ALDERLAKE)
-       {
-               .flags = FLAG_SOF | FLAG_SOF_ONLY_IF_DMIC_OR_SOUNDWIRE,
-               .device = PCI_DEVICE_ID_INTEL_HDA_ADL_S,
-       },
-       {
-               .flags = FLAG_SOF | FLAG_SOF_ONLY_IF_DMIC_OR_SOUNDWIRE,
-               .device = PCI_DEVICE_ID_INTEL_HDA_RPL_S,
-       },
-       {
-               .flags = FLAG_SOF,
-               .device = PCI_DEVICE_ID_INTEL_HDA_ADL_P,
-               .dmi_table = (const struct dmi_system_id []) {
-                       {
-                               .ident = "Google Chromebooks",
-                               .matches = {
-                                       DMI_MATCH(DMI_SYS_VENDOR, "Google"),
-                               }
-                       },
-                       {}
-               }
-       },
-       {
-               .flags = FLAG_SOF,
-               .device = PCI_DEVICE_ID_INTEL_HDA_ADL_P,
-               .codec_hid =  &essx_83x6,
-       },
-       {
-               .flags = FLAG_SOF | FLAG_SOF_ONLY_IF_DMIC_OR_SOUNDWIRE,
-               .device = PCI_DEVICE_ID_INTEL_HDA_ADL_P,
-       },
-       {
-               .flags = FLAG_SOF | FLAG_SOF_ONLY_IF_DMIC_OR_SOUNDWIRE,
-               .device = PCI_DEVICE_ID_INTEL_HDA_ADL_PX,
-       },
-       {
-               .flags = FLAG_SOF,
-               .device = PCI_DEVICE_ID_INTEL_HDA_ADL_PS,
-               .codec_hid =  &essx_83x6,
-       },
-       {
-               .flags = FLAG_SOF | FLAG_SOF_ONLY_IF_DMIC_OR_SOUNDWIRE,
-               .device = PCI_DEVICE_ID_INTEL_HDA_ADL_PS,
-       },
-       {
-               .flags = FLAG_SOF | FLAG_SOF_ONLY_IF_DMIC_OR_SOUNDWIRE,
-               .device = PCI_DEVICE_ID_INTEL_HDA_ADL_M,
-       },
-       {
-               .flags = FLAG_SOF,
-               .device = PCI_DEVICE_ID_INTEL_HDA_ADL_N,
-               .dmi_table = (const struct dmi_system_id []) {
-                       {
-                               .ident = "Google Chromebooks",
-                               .matches = {
-                                       DMI_MATCH(DMI_SYS_VENDOR, "Google"),
-                               }
-                       },
-                       {}
-               }
-       },
-       {
-               .flags = FLAG_SOF | FLAG_SOF_ONLY_IF_DMIC_OR_SOUNDWIRE,
-               .device = PCI_DEVICE_ID_INTEL_HDA_ADL_N,
-       },
-       {
-               .flags = FLAG_SOF,
-               .device = PCI_DEVICE_ID_INTEL_HDA_RPL_P_0,
-               .dmi_table = (const struct dmi_system_id []) {
-                       {
-                               .ident = "Google Chromebooks",
-                               .matches = {
-                                       DMI_MATCH(DMI_SYS_VENDOR, "Google"),
-                               }
-                       },
-                       {}
-               }
-       },
-       {
-               .flags = FLAG_SOF | FLAG_SOF_ONLY_IF_DMIC_OR_SOUNDWIRE,
-               .device = PCI_DEVICE_ID_INTEL_HDA_RPL_P_0,
-       },
-       {
-               .flags = FLAG_SOF,
-               .device = PCI_DEVICE_ID_INTEL_HDA_RPL_P_1,
-               .dmi_table = (const struct dmi_system_id []) {
-                       {
-                               .ident = "Google Chromebooks",
-                               .matches = {
-                                       DMI_MATCH(DMI_SYS_VENDOR, "Google"),
-                               }
-                       },
-                       {}
-               }
-       },
-       {
-               .flags = FLAG_SOF | FLAG_SOF_ONLY_IF_DMIC_OR_SOUNDWIRE,
-               .device = PCI_DEVICE_ID_INTEL_HDA_RPL_P_1,
-       },
-       {
-               .flags = FLAG_SOF | FLAG_SOF_ONLY_IF_DMIC_OR_SOUNDWIRE,
-               .device = PCI_DEVICE_ID_INTEL_HDA_RPL_M,
-       },
-       {
-               .flags = FLAG_SOF | FLAG_SOF_ONLY_IF_DMIC_OR_SOUNDWIRE,
-               .device = PCI_DEVICE_ID_INTEL_HDA_RPL_PX,
-       },
-#endif
-
-/* Meteor Lake */
-#if IS_ENABLED(CONFIG_SND_SOC_SOF_METEORLAKE)
-       /* Meteorlake-P */
-       {
-               .flags = FLAG_SOF | FLAG_SOF_ONLY_IF_DMIC_OR_SOUNDWIRE,
-               .device = PCI_DEVICE_ID_INTEL_HDA_MTL,
-       },
-       /* ArrowLake-S */
-       {
-               .flags = FLAG_SOF | FLAG_SOF_ONLY_IF_DMIC_OR_SOUNDWIRE,
-               .device = PCI_DEVICE_ID_INTEL_HDA_ARL_S,
-       },
-       /* ArrowLake */
-       {
-               .flags = FLAG_SOF | FLAG_SOF_ONLY_IF_DMIC_OR_SOUNDWIRE,
-               .device = PCI_DEVICE_ID_INTEL_HDA_ARL,
-       },
-#endif
-
-/* Lunar Lake */
-#if IS_ENABLED(CONFIG_SND_SOC_SOF_LUNARLAKE)
-       /* Lunarlake-P */
-       {
-               .flags = FLAG_SOF | FLAG_SOF_ONLY_IF_DMIC_OR_SOUNDWIRE,
-               .device = PCI_DEVICE_ID_INTEL_HDA_LNL_P,
-       },
-#endif
-
-       /* Panther Lake, Wildcat Lake */
-#if IS_ENABLED(CONFIG_SND_SOC_SOF_PANTHERLAKE)
-       {
-               .flags = FLAG_SOF | FLAG_SOF_ONLY_IF_DMIC_OR_SOUNDWIRE,
-               .device = PCI_DEVICE_ID_INTEL_HDA_PTL,
-       },
-       {
-               .flags = FLAG_SOF | FLAG_SOF_ONLY_IF_DMIC_OR_SOUNDWIRE,
-               .device = PCI_DEVICE_ID_INTEL_HDA_PTL_H,
-       },
-       {
-               .flags = FLAG_SOF | FLAG_SOF_ONLY_IF_DMIC_OR_SOUNDWIRE,
-               .device = PCI_DEVICE_ID_INTEL_HDA_WCL,
-       },
-
-#endif
-
-};
-
-static const struct config_entry *snd_intel_dsp_find_config
-               (struct pci_dev *pci, const struct config_entry *table, u32 len)
-{
-       u16 device;
-
-       device = pci->device;
-       for (; len > 0; len--, table++) {
-               if (table->device != device)
-                       continue;
-               if (table->dmi_table && !dmi_check_system(table->dmi_table))
-                       continue;
-               if (table->codec_hid) {
-                       int i;
-
-                       for (i = 0; i < table->codec_hid->num_codecs; i++) {
-                               struct nhlt_acpi_table *nhlt;
-                               bool ssp_found = false;
-
-                               if (!acpi_dev_present(table->codec_hid->codecs[i], NULL, -1))
-                                       continue;
-
-                               nhlt = intel_nhlt_init(&pci->dev);
-                               if (!nhlt) {
-                                       dev_warn(&pci->dev, "%s: NHLT table not found, skipped HID %s\n",
-                                                __func__, table->codec_hid->codecs[i]);
-                                       continue;
-                               }
-
-                               if (intel_nhlt_has_endpoint_type(nhlt, NHLT_LINK_SSP) &&
-                                   intel_nhlt_ssp_endpoint_mask(nhlt, NHLT_DEVICE_I2S))
-                                       ssp_found = true;
-
-                               intel_nhlt_free(nhlt);
-
-                               if (ssp_found)
-                                       break;
-
-                               dev_warn(&pci->dev, "%s: no valid SSP found for HID %s, skipped\n",
-                                        __func__, table->codec_hid->codecs[i]);
-                       }
-                       if (i == table->codec_hid->num_codecs)
-                               continue;
-               }
-               return table;
-       }
-       return NULL;
-}
-
-static int snd_intel_dsp_check_dmic(struct pci_dev *pci)
-{
-       int ret = 0;
-
-       acpi_nhlt_get_gbl_table();
-
-       if (acpi_nhlt_find_endpoint(ACPI_NHLT_LINKTYPE_PDM, -1, -1, -1))
-               ret = 1;
-
-       acpi_nhlt_put_gbl_table();
-
-       return ret;
-}
-
-#if IS_ENABLED(CONFIG_SND_SOC_SOF_INTEL_SOUNDWIRE)
-static int snd_intel_dsp_check_soundwire(struct pci_dev *pci)
-{
-       struct sdw_intel_acpi_info info;
-       acpi_handle handle;
-       int ret;
-
-       handle = ACPI_HANDLE(&pci->dev);
-
-       ret = sdw_intel_acpi_scan(handle, &info);
-       if (ret < 0)
-               return ret;
-
-       return info.link_mask;
-}
-#else
-static int snd_intel_dsp_check_soundwire(struct pci_dev *pci)
-{
-       return 0;
-}
-#endif
-
-int snd_intel_dsp_driver_probe(struct pci_dev *pci)
-{
-       const struct config_entry *cfg;
-
-       /* Intel vendor only */
-       if (pci->vendor != PCI_VENDOR_ID_INTEL)
-               return SND_INTEL_DSP_DRIVER_ANY;
-
-       /*
-        * Legacy devices don't have a PCI-based DSP and use HDaudio
-        * for HDMI/DP support, ignore kernel parameter
-        */
-       switch (pci->device) {
-       case PCI_DEVICE_ID_INTEL_HDA_BDW:
-       case PCI_DEVICE_ID_INTEL_HDA_HSW_0:
-       case PCI_DEVICE_ID_INTEL_HDA_HSW_2:
-       case PCI_DEVICE_ID_INTEL_HDA_HSW_3:
-       case PCI_DEVICE_ID_INTEL_HDA_BYT:
-       case PCI_DEVICE_ID_INTEL_HDA_BSW:
-               return SND_INTEL_DSP_DRIVER_ANY;
-       }
-
-       if (dsp_driver > 0 && dsp_driver <= SND_INTEL_DSP_DRIVER_LAST)
-               return dsp_driver;
-
-       /*
-        * detect DSP by checking class/subclass/prog-id information
-        * class=04 subclass 03 prog-if 00: no DSP, use legacy driver
-        * class=04 subclass 01 prog-if 00: DSP is present
-        *  (and may be required e.g. for DMIC or SSP support)
-        * class=04 subclass 03 prog-if 80: use DSP or legacy mode
-        */
-       if (pci->class == 0x040300)
-               return SND_INTEL_DSP_DRIVER_LEGACY;
-       if (pci->class != 0x040100 && pci->class != 0x040380) {
-               dev_err(&pci->dev, "Unknown PCI class/subclass/prog-if information (0x%06x) found, selecting HDAudio legacy driver\n", pci->class);
-               return SND_INTEL_DSP_DRIVER_LEGACY;
-       }
-
-       dev_dbg(&pci->dev, "DSP detected with PCI class/subclass/prog-if info 0x%06x\n", pci->class);
-
-       /* find the configuration for the specific device */
-       cfg = snd_intel_dsp_find_config(pci, config_table, ARRAY_SIZE(config_table));
-       if (!cfg)
-               return SND_INTEL_DSP_DRIVER_ANY;
-
-       if (cfg->flags & FLAG_SOF) {
-               if (cfg->flags & FLAG_SOF_ONLY_IF_SOUNDWIRE &&
-                   snd_intel_dsp_check_soundwire(pci) > 0) {
-                       dev_info_once(&pci->dev, "SoundWire enabled on CannonLake+ platform, using SOF driver\n");
-                       return SND_INTEL_DSP_DRIVER_SOF;
-               }
-               if (cfg->flags & FLAG_SOF_ONLY_IF_DMIC &&
-                   snd_intel_dsp_check_dmic(pci)) {
-                       dev_info_once(&pci->dev, "Digital mics found on Skylake+ platform, using SOF driver\n");
-                       return SND_INTEL_DSP_DRIVER_SOF;
-               }
-               if (!(cfg->flags & FLAG_SOF_ONLY_IF_DMIC_OR_SOUNDWIRE))
-                       return SND_INTEL_DSP_DRIVER_SOF;
-       }
-
-
-       if (cfg->flags & FLAG_SST) {
-               if (cfg->flags & FLAG_SST_ONLY_IF_DMIC) {
-                       if (snd_intel_dsp_check_dmic(pci)) {
-                               dev_info_once(&pci->dev, "Digital mics found on Skylake+ platform, using SST driver\n");
-                               return SND_INTEL_DSP_DRIVER_SST;
-                       }
-               } else {
-                       return SND_INTEL_DSP_DRIVER_SST;
-               }
-       }
-
-       return SND_INTEL_DSP_DRIVER_LEGACY;
-}
-EXPORT_SYMBOL_GPL(snd_intel_dsp_driver_probe);
-
-/* Should we default to SOF or SST for BYT/CHT ? */
-#if IS_ENABLED(CONFIG_SND_INTEL_BYT_PREFER_SOF) || \
-    !IS_ENABLED(CONFIG_SND_SST_ATOM_HIFI2_PLATFORM_ACPI)
-#define FLAG_SST_OR_SOF_BYT    FLAG_SOF
-#else
-#define FLAG_SST_OR_SOF_BYT    FLAG_SST
-#endif
-
-/*
- * configuration table
- * - the order of similar ACPI ID entries is important!
- * - the first successful match will win
- */
-static const struct config_entry acpi_config_table[] = {
-#if IS_ENABLED(CONFIG_SND_SST_ATOM_HIFI2_PLATFORM_ACPI) || \
-    IS_ENABLED(CONFIG_SND_SOC_SOF_BAYTRAIL)
-/* BayTrail */
-       {
-               .flags = FLAG_SST_OR_SOF_BYT,
-               .acpi_hid = "LPE0F28",
-       },
-       {
-               .flags = FLAG_SST_OR_SOF_BYT,
-               .acpi_hid = "80860F28",
-       },
-/* CherryTrail */
-       {
-               .flags = FLAG_SST_OR_SOF_BYT,
-               .acpi_hid = "808622A8",
-       },
-#endif
-/* Broadwell */
-#if IS_ENABLED(CONFIG_SND_SOC_INTEL_CATPT)
-       {
-               .flags = FLAG_SST,
-               .acpi_hid = "INT3438"
-       },
-#endif
-#if IS_ENABLED(CONFIG_SND_SOC_SOF_BROADWELL)
-       {
-               .flags = FLAG_SOF,
-               .acpi_hid = "INT3438"
-       },
-#endif
-/* Haswell - not supported by SOF but added for consistency */
-#if IS_ENABLED(CONFIG_SND_SOC_INTEL_CATPT)
-       {
-               .flags = FLAG_SST,
-               .acpi_hid = "INT33C8"
-       },
-#endif
-};
-
-static const struct config_entry *snd_intel_acpi_dsp_find_config(const u8 acpi_hid[ACPI_ID_LEN],
-                                                                const struct config_entry *table,
-                                                                u32 len)
-{
-       for (; len > 0; len--, table++) {
-               if (memcmp(table->acpi_hid, acpi_hid, ACPI_ID_LEN))
-                       continue;
-               if (table->dmi_table && !dmi_check_system(table->dmi_table))
-                       continue;
-               return table;
-       }
-       return NULL;
-}
-
-int snd_intel_acpi_dsp_driver_probe(struct device *dev, const u8 acpi_hid[ACPI_ID_LEN])
-{
-       const struct config_entry *cfg;
-
-       if (dsp_driver > SND_INTEL_DSP_DRIVER_LEGACY && dsp_driver <= SND_INTEL_DSP_DRIVER_LAST)
-               return dsp_driver;
-
-       if (dsp_driver == SND_INTEL_DSP_DRIVER_LEGACY) {
-               dev_warn(dev, "dsp_driver parameter %d not supported, using automatic detection\n",
-                        SND_INTEL_DSP_DRIVER_LEGACY);
-       }
-
-       /* find the configuration for the specific device */
-       cfg = snd_intel_acpi_dsp_find_config(acpi_hid,  acpi_config_table,
-                                            ARRAY_SIZE(acpi_config_table));
-       if (!cfg)
-               return SND_INTEL_DSP_DRIVER_ANY;
-
-       if (cfg->flags & FLAG_SST)
-               return SND_INTEL_DSP_DRIVER_SST;
-
-       if (cfg->flags & FLAG_SOF)
-               return SND_INTEL_DSP_DRIVER_SOF;
-
-       return SND_INTEL_DSP_DRIVER_SST;
-}
-EXPORT_SYMBOL_GPL(snd_intel_acpi_dsp_driver_probe);
-
-MODULE_LICENSE("GPL v2");
-MODULE_DESCRIPTION("Intel DSP config driver");
-MODULE_IMPORT_NS("SND_INTEL_SOUNDWIRE_ACPI");
diff --git a/sound/hda/intel-nhlt.c b/sound/hda/intel-nhlt.c
deleted file mode 100644 (file)
index 6d72a87..0000000
+++ /dev/null
@@ -1,388 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0-only
-// Copyright (c) 2015-2019 Intel Corporation
-
-#include <linux/acpi.h>
-#include <sound/intel-nhlt.h>
-
-struct nhlt_acpi_table *intel_nhlt_init(struct device *dev)
-{
-       struct nhlt_acpi_table *nhlt;
-       acpi_status status;
-
-       status = acpi_get_table(ACPI_SIG_NHLT, 0,
-                               (struct acpi_table_header **)&nhlt);
-       if (ACPI_FAILURE(status)) {
-               dev_warn(dev, "NHLT table not found\n");
-               return NULL;
-       }
-
-       return nhlt;
-}
-EXPORT_SYMBOL_GPL(intel_nhlt_init);
-
-void intel_nhlt_free(struct nhlt_acpi_table *nhlt)
-{
-       acpi_put_table((struct acpi_table_header *)nhlt);
-}
-EXPORT_SYMBOL_GPL(intel_nhlt_free);
-
-int intel_nhlt_get_dmic_geo(struct device *dev, struct nhlt_acpi_table *nhlt)
-{
-       struct nhlt_endpoint *epnt;
-       struct nhlt_dmic_array_config *cfg;
-       struct nhlt_vendor_dmic_array_config *cfg_vendor;
-       struct nhlt_fmt *fmt_configs;
-       unsigned int dmic_geo = 0;
-       u16 max_ch = 0;
-       u8 i, j;
-
-       if (!nhlt)
-               return 0;
-
-       if (nhlt->header.length <= sizeof(struct acpi_table_header)) {
-               dev_warn(dev, "Invalid DMIC description table\n");
-               return 0;
-       }
-
-       for (j = 0, epnt = nhlt->desc; j < nhlt->endpoint_count; j++,
-            epnt = (struct nhlt_endpoint *)((u8 *)epnt + epnt->length)) {
-
-               if (epnt->linktype != NHLT_LINK_DMIC)
-                       continue;
-
-               cfg = (struct nhlt_dmic_array_config  *)(epnt->config.caps);
-               fmt_configs = (struct nhlt_fmt *)(epnt->config.caps + epnt->config.size);
-
-               /* find max number of channels based on format_configuration */
-               if (fmt_configs->fmt_count) {
-                       struct nhlt_fmt_cfg *fmt_cfg = fmt_configs->fmt_config;
-
-                       dev_dbg(dev, "found %d format definitions\n",
-                               fmt_configs->fmt_count);
-
-                       for (i = 0; i < fmt_configs->fmt_count; i++) {
-                               struct wav_fmt_ext *fmt_ext;
-
-                               fmt_ext = &fmt_cfg->fmt_ext;
-
-                               if (fmt_ext->fmt.channels > max_ch)
-                                       max_ch = fmt_ext->fmt.channels;
-
-                               /* Move to the next nhlt_fmt_cfg */
-                               fmt_cfg = (struct nhlt_fmt_cfg *)(fmt_cfg->config.caps +
-                                                                 fmt_cfg->config.size);
-                       }
-                       dev_dbg(dev, "max channels found %d\n", max_ch);
-               } else {
-                       dev_dbg(dev, "No format information found\n");
-               }
-
-               if (cfg->device_config.config_type != NHLT_CONFIG_TYPE_MIC_ARRAY) {
-                       dmic_geo = max_ch;
-               } else {
-                       switch (cfg->array_type) {
-                       case NHLT_MIC_ARRAY_2CH_SMALL:
-                       case NHLT_MIC_ARRAY_2CH_BIG:
-                               dmic_geo = MIC_ARRAY_2CH;
-                               break;
-
-                       case NHLT_MIC_ARRAY_4CH_1ST_GEOM:
-                       case NHLT_MIC_ARRAY_4CH_L_SHAPED:
-                       case NHLT_MIC_ARRAY_4CH_2ND_GEOM:
-                               dmic_geo = MIC_ARRAY_4CH;
-                               break;
-                       case NHLT_MIC_ARRAY_VENDOR_DEFINED:
-                               cfg_vendor = (struct nhlt_vendor_dmic_array_config *)cfg;
-                               dmic_geo = cfg_vendor->nb_mics;
-                               break;
-                       default:
-                               dev_warn(dev, "%s: undefined DMIC array_type 0x%0x\n",
-                                        __func__, cfg->array_type);
-                       }
-
-                       if (dmic_geo > 0) {
-                               dev_dbg(dev, "Array with %d dmics\n", dmic_geo);
-                       }
-                       if (max_ch > dmic_geo) {
-                               dev_dbg(dev, "max channels %d exceed dmic number %d\n",
-                                       max_ch, dmic_geo);
-                       }
-               }
-       }
-
-       dev_dbg(dev, "dmic number %d max_ch %d\n", dmic_geo, max_ch);
-
-       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);
-
-int intel_nhlt_ssp_endpoint_mask(struct nhlt_acpi_table *nhlt, u8 device_type)
-{
-       struct nhlt_endpoint *epnt;
-       int ssp_mask = 0;
-       int i;
-
-       if (!nhlt || (device_type != NHLT_DEVICE_BT && device_type != NHLT_DEVICE_I2S))
-               return 0;
-
-       epnt = (struct nhlt_endpoint *)nhlt->desc;
-       for (i = 0; i < nhlt->endpoint_count; i++) {
-               if (epnt->linktype == NHLT_LINK_SSP && epnt->device_type == device_type) {
-                       /* for SSP the virtual bus id is the SSP port */
-                       ssp_mask |= BIT(epnt->virtual_bus_id);
-               }
-               epnt = (struct nhlt_endpoint *)((u8 *)epnt + epnt->length);
-       }
-
-       return ssp_mask;
-}
-EXPORT_SYMBOL(intel_nhlt_ssp_endpoint_mask);
-
-#define SSP_BLOB_V1_0_SIZE             84
-#define SSP_BLOB_V1_0_MDIVC_OFFSET     19 /* offset in u32 */
-
-#define SSP_BLOB_V1_5_SIZE             96
-#define SSP_BLOB_V1_5_MDIVC_OFFSET     21 /* offset in u32 */
-#define SSP_BLOB_VER_1_5               0xEE000105
-
-#define SSP_BLOB_V2_0_SIZE             88
-#define SSP_BLOB_V2_0_MDIVC_OFFSET     20 /* offset in u32 */
-#define SSP_BLOB_VER_2_0               0xEE000200
-
-int intel_nhlt_ssp_mclk_mask(struct nhlt_acpi_table *nhlt, int ssp_num)
-{
-       struct nhlt_endpoint *epnt;
-       struct nhlt_fmt *fmt;
-       struct nhlt_fmt_cfg *cfg;
-       int mclk_mask = 0;
-       int i, j;
-
-       if (!nhlt)
-               return 0;
-
-       epnt = (struct nhlt_endpoint *)nhlt->desc;
-       for (i = 0; i < nhlt->endpoint_count; i++) {
-
-               /* we only care about endpoints connected to an audio codec over SSP */
-               if (epnt->linktype == NHLT_LINK_SSP &&
-                   epnt->device_type == NHLT_DEVICE_I2S &&
-                   epnt->virtual_bus_id == ssp_num) {
-
-                       fmt = (struct nhlt_fmt *)(epnt->config.caps + epnt->config.size);
-                       cfg = fmt->fmt_config;
-
-                       /*
-                        * In theory all formats should use the same MCLK but it doesn't hurt to
-                        * double-check that the configuration is consistent
-                        */
-                       for (j = 0; j < fmt->fmt_count; j++) {
-                               u32 *blob;
-                               int mdivc_offset;
-                               int size;
-
-                               /* first check we have enough data to read the blob type */
-                               if (cfg->config.size < 8)
-                                       return -EINVAL;
-
-                               blob = (u32 *)cfg->config.caps;
-
-                               if (blob[1] == SSP_BLOB_VER_2_0) {
-                                       mdivc_offset = SSP_BLOB_V2_0_MDIVC_OFFSET;
-                                       size = SSP_BLOB_V2_0_SIZE;
-                               } else if (blob[1] == SSP_BLOB_VER_1_5) {
-                                       mdivc_offset = SSP_BLOB_V1_5_MDIVC_OFFSET;
-                                       size = SSP_BLOB_V1_5_SIZE;
-                               } else {
-                                       mdivc_offset = SSP_BLOB_V1_0_MDIVC_OFFSET;
-                                       size = SSP_BLOB_V1_0_SIZE;
-                               }
-
-                               /* make sure we have enough data for the fixed part of the blob */
-                               if (cfg->config.size < size)
-                                       return -EINVAL;
-
-                               mclk_mask |=  blob[mdivc_offset] & GENMASK(1, 0);
-
-                               cfg = (struct nhlt_fmt_cfg *)(cfg->config.caps + cfg->config.size);
-                       }
-               }
-               epnt = (struct nhlt_endpoint *)((u8 *)epnt + epnt->length);
-       }
-
-       /* make sure only one MCLK is used */
-       if (hweight_long(mclk_mask) != 1)
-               return -EINVAL;
-
-       return mclk_mask;
-}
-EXPORT_SYMBOL(intel_nhlt_ssp_mclk_mask);
-
-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, bool ignore_vbps)
-{
-       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);
-
-               /*
-                * When looking for exact match of configuration ignore the vbps
-                * from NHLT table when ignore_vbps is true
-                */
-               if (wfmt->channels == num_ch && wfmt->samples_per_sec == rate &&
-                   (ignore_vbps || 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;
-       bool ignore_vbps = false;
-       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);
-       if (link_type == NHLT_LINK_DMIC && bps == 32 && (vbps == 24 || vbps == 32)) {
-               /*
-                * The DMIC hardware supports only one type of 32 bits sample
-                * size, which is 24 bit sampling on the MSB side and bits[1:0]
-                * are used for indicating the channel number.
-                * It has been observed that some NHLT tables have the vbps
-                * specified as 32 while some uses 24.
-                * The format these variations describe are identical, the
-                * hardware is configured and behaves the same way.
-                * Note: when the samples assumed to be vbps=32 then the 'noise'
-                * introduced by the lower two bits (channel number) have no
-                * real life implication on audio quality.
-                */
-               dev_dbg(dev,
-                       "  ch=%d fmt=%d rate=%d (vbps is ignored for DMIC 32bit format)\n",
-                       num_ch, bps, rate);
-               ignore_vbps = true;
-       } else {
-               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, ignore_vbps);
-                       if (cfg)
-                               return cfg;
-               }
-
-               epnt = (struct nhlt_endpoint *)((u8 *)epnt + epnt->length);
-       }
-
-       return NULL;
-}
-EXPORT_SYMBOL(intel_nhlt_get_endpoint_blob);
-
-int intel_nhlt_ssp_device_type(struct device *dev, struct nhlt_acpi_table *nhlt,
-                              u8 virtual_bus_id)
-{
-       struct nhlt_endpoint *epnt;
-       int i;
-
-       if (!nhlt) {
-               dev_err(dev, "%s: NHLT table is missing (query for SSP%d)\n",
-                       __func__, virtual_bus_id);
-               return -EINVAL;
-       }
-
-       epnt = (struct nhlt_endpoint *)nhlt->desc;
-       for (i = 0; i < nhlt->endpoint_count; i++) {
-               /* for SSP link the virtual bus id is the SSP port number */
-               if (epnt->linktype == NHLT_LINK_SSP &&
-                   epnt->virtual_bus_id == virtual_bus_id) {
-                       dev_dbg(dev, "SSP%d: dev_type=%d\n", virtual_bus_id,
-                               epnt->device_type);
-                       return epnt->device_type;
-               }
-
-               epnt = (struct nhlt_endpoint *)((u8 *)epnt + epnt->length);
-       }
-
-       dev_err(dev, "%s: No match for SSP%d in NHLT table\n", __func__,
-               virtual_bus_id);
-
-       dev_dbg(dev, "Available endpoints:\n");
-       epnt = (struct nhlt_endpoint *)nhlt->desc;
-       for (i = 0; i < nhlt->endpoint_count; i++) {
-               dev_dbg(dev,
-                       "%d: link_type: %d, vbus_id: %d, dir: %d, dev_type: %d\n",
-                       i, epnt->linktype, epnt->virtual_bus_id,
-                       epnt->direction, epnt->device_type);
-
-               epnt = (struct nhlt_endpoint *)((u8 *)epnt + epnt->length);
-       }
-
-       return -EINVAL;
-}
-EXPORT_SYMBOL(intel_nhlt_ssp_device_type);
diff --git a/sound/hda/intel-sdw-acpi.c b/sound/hda/intel-sdw-acpi.c
deleted file mode 100644 (file)
index d351113..0000000
+++ /dev/null
@@ -1,204 +0,0 @@
-// SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause)
-// Copyright(c) 2015-2021 Intel Corporation.
-
-/*
- * SDW Intel ACPI scan helpers
- */
-
-#include <linux/acpi.h>
-#include <linux/bits.h>
-#include <linux/bitfield.h>
-#include <linux/device.h>
-#include <linux/errno.h>
-#include <linux/export.h>
-#include <linux/module.h>
-#include <linux/property.h>
-#include <linux/soundwire/sdw_intel.h>
-#include <linux/string.h>
-
-#define SDW_LINK_TYPE          4 /* from Intel ACPI documentation */
-
-static int ctrl_link_mask;
-module_param_named(sdw_link_mask, ctrl_link_mask, int, 0444);
-MODULE_PARM_DESC(sdw_link_mask, "Intel link mask (one bit per link)");
-
-static ulong ctrl_addr = 0x40000000;
-module_param_named(sdw_ctrl_addr, ctrl_addr, ulong, 0444);
-MODULE_PARM_DESC(sdw_ctrl_addr, "Intel SoundWire Controller _ADR");
-
-static bool is_link_enabled(struct fwnode_handle *fw_node, u8 idx)
-{
-       struct fwnode_handle *link;
-       char name[32];
-       u32 quirk_mask = 0;
-
-       /* Find master handle */
-       snprintf(name, sizeof(name),
-                "mipi-sdw-link-%hhu-subproperties", idx);
-
-       link = fwnode_get_named_child_node(fw_node, name);
-       if (!link)
-               return false;
-
-       fwnode_property_read_u32(link,
-                                "intel-quirk-mask",
-                                &quirk_mask);
-
-       fwnode_handle_put(link);
-
-       if (quirk_mask & SDW_INTEL_QUIRK_MASK_BUS_DISABLE)
-               return false;
-
-       return true;
-}
-
-static int
-sdw_intel_scan_controller(struct sdw_intel_acpi_info *info)
-{
-       struct acpi_device *adev = acpi_fetch_acpi_dev(info->handle);
-       struct fwnode_handle *fwnode;
-       unsigned long list;
-       unsigned int i;
-       u32 count;
-       u32 tmp;
-       int ret;
-
-       if (!adev)
-               return -EINVAL;
-
-       fwnode = acpi_fwnode_handle(adev);
-
-       /*
-        * Found controller, find links supported
-        *
-        * In theory we could check the number of links supported in
-        * hardware, but in that step we cannot assume SoundWire IP is
-        * powered.
-        *
-        * In addition, if the BIOS doesn't even provide this
-        * 'master-count' property then all the inits based on link
-        * masks will fail as well.
-        *
-        * We will check the hardware capabilities in the startup() step
-        */
-       ret = fwnode_property_read_u32(fwnode, "mipi-sdw-manager-list", &tmp);
-       if (ret) {
-               ret = fwnode_property_read_u32(fwnode, "mipi-sdw-master-count", &count);
-               if (ret) {
-                       dev_err(&adev->dev,
-                               "Failed to read mipi-sdw-master-count: %d\n",
-                               ret);
-                       return ret;
-               }
-               list = GENMASK(count - 1, 0);
-       } else {
-               list = tmp;
-               count = hweight32(list);
-       }
-
-       /* Check count is within bounds */
-       if (count > SDW_INTEL_MAX_LINKS) {
-               dev_err(&adev->dev, "Link count %d exceeds max %d\n",
-                       count, SDW_INTEL_MAX_LINKS);
-               return -EINVAL;
-       }
-
-       if (!count) {
-               dev_warn(&adev->dev, "No SoundWire links detected\n");
-               return -EINVAL;
-       }
-       dev_dbg(&adev->dev, "ACPI reports %d SDW Link devices\n", count);
-
-       info->count = count;
-       info->link_mask = 0;
-
-       for_each_set_bit(i, &list, SDW_INTEL_MAX_LINKS) {
-               if (ctrl_link_mask && !(ctrl_link_mask & BIT(i))) {
-                       dev_dbg(&adev->dev,
-                               "Link %d masked, will not be enabled\n", i);
-                       continue;
-               }
-
-               if (!is_link_enabled(fwnode, i)) {
-                       dev_dbg(&adev->dev,
-                               "Link %d not selected in firmware\n", i);
-                       continue;
-               }
-
-               info->link_mask |= BIT(i);
-       }
-
-       return 0;
-}
-
-static acpi_status sdw_intel_acpi_cb(acpi_handle handle, u32 level,
-                                    void *cdata, void **return_value)
-{
-       struct sdw_intel_acpi_info *info = cdata;
-       u64 adr;
-       int ret;
-
-       ret = acpi_get_local_u64_address(handle, &adr);
-       if (ret < 0)
-               return AE_OK; /* keep going */
-
-       if (!acpi_fetch_acpi_dev(handle)) {
-               pr_err("%s: Couldn't find ACPI handle\n", __func__);
-               return AE_NOT_FOUND;
-       }
-
-       /*
-        * On some Intel platforms, multiple children of the HDAS
-        * device can be found, but only one of them is the SoundWire
-        * controller. The SNDW device is always exposed with
-        * Name(_ADR, 0x40000000), with bits 31..28 representing the
-        * SoundWire link so filter accordingly
-        */
-       if (FIELD_GET(GENMASK(31, 28), adr) != SDW_LINK_TYPE)
-               return AE_OK; /* keep going */
-
-       if (adr != ctrl_addr)
-               return AE_OK; /* keep going */
-
-       /* found the correct SoundWire controller */
-       info->handle = handle;
-
-       /* device found, stop namespace walk */
-       return AE_CTRL_TERMINATE;
-}
-
-/**
- * sdw_intel_acpi_scan() - SoundWire Intel init routine
- * @parent_handle: ACPI parent handle
- * @info: description of what firmware/DSDT tables expose
- *
- * This scans the namespace and queries firmware to figure out which
- * links to enable. A follow-up use of sdw_intel_probe() and
- * sdw_intel_startup() is required for creation of devices and bus
- * startup
- */
-int sdw_intel_acpi_scan(acpi_handle parent_handle,
-                       struct sdw_intel_acpi_info *info)
-{
-       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, 2,
-                                    sdw_intel_acpi_cb,
-                                    NULL, info, NULL);
-       if (ACPI_FAILURE(status) || info->handle == NULL)
-               return -ENODEV;
-
-       return sdw_intel_scan_controller(info);
-}
-EXPORT_SYMBOL_NS(sdw_intel_acpi_scan, "SND_INTEL_SOUNDWIRE_ACPI");
-
-MODULE_LICENSE("Dual BSD/GPL");
-MODULE_DESCRIPTION("Intel Soundwire ACPI helpers");
diff --git a/sound/hda/local.h b/sound/hda/local.h
deleted file mode 100644 (file)
index 5f03b20..0000000
+++ /dev/null
@@ -1,25 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0 */
-/*
- * Local helper macros and functions for HD-audio core drivers
- */
-
-#ifndef __HDAC_LOCAL_H
-#define __HDAC_LOCAL_H
-
-extern const struct attribute_group *hdac_dev_attr_groups[];
-int hda_widget_sysfs_init(struct hdac_device *codec);
-int hda_widget_sysfs_reinit(struct hdac_device *codec, hda_nid_t start_nid,
-                           int num_nodes);
-void hda_widget_sysfs_exit(struct hdac_device *codec);
-
-int snd_hdac_bus_add_device(struct hdac_bus *bus, struct hdac_device *codec);
-void snd_hdac_bus_remove_device(struct hdac_bus *bus,
-                               struct hdac_device *codec);
-void snd_hdac_bus_queue_event(struct hdac_bus *bus, u32 res, u32 res_ex);
-int snd_hdac_bus_exec_verb(struct hdac_bus *bus, unsigned int addr,
-                          unsigned int cmd, unsigned int *res);
-
-int snd_hdac_exec_verb(struct hdac_device *codec, unsigned int cmd,
-                      unsigned int flags, unsigned int *res);
-
-#endif /* __HDAC_LOCAL_H */
diff --git a/sound/hda/trace.c b/sound/hda/trace.c
deleted file mode 100644 (file)
index ca2d6bd..0000000
+++ /dev/null
@@ -1,6 +0,0 @@
-/*
- * tracepoint definitions for HD-audio core drivers
- */
-
-#define CREATE_TRACE_POINTS
-#include "trace.h"
diff --git a/sound/hda/trace.h b/sound/hda/trace.h
deleted file mode 100644 (file)
index 280c42f..0000000
+++ /dev/null
@@ -1,101 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0 */
-#undef TRACE_SYSTEM
-#define TRACE_SYSTEM hda
-
-#if !defined(__HDAC_TRACE_H) || defined(TRACE_HEADER_MULTI_READ)
-#define __HDAC_TRACE_H
-
-#include <linux/tracepoint.h>
-#include <linux/device.h>
-#include <sound/hdaudio.h>
-
-#ifndef HDAC_MSG_MAX
-#define HDAC_MSG_MAX   500
-#endif
-
-struct hdac_bus;
-struct hdac_codec;
-
-TRACE_EVENT(hda_send_cmd,
-       TP_PROTO(struct hdac_bus *bus, unsigned int cmd),
-       TP_ARGS(bus, cmd),
-       TP_STRUCT__entry(
-               __string(name, dev_name((bus)->dev))
-               __field(u32, cmd)
-       ),
-       TP_fast_assign(
-               __assign_str(name);
-               __entry->cmd = cmd;
-       ),
-       TP_printk("[%s:%d] val=0x%08x", __get_str(name), __entry->cmd >> 28, __entry->cmd)
-);
-
-TRACE_EVENT(hda_get_response,
-       TP_PROTO(struct hdac_bus *bus, unsigned int addr, unsigned int res),
-       TP_ARGS(bus, addr, res),
-       TP_STRUCT__entry(
-               __string(name, dev_name((bus)->dev))
-               __field(u32, addr)
-               __field(u32, res)
-       ),
-       TP_fast_assign(
-               __assign_str(name);
-               __entry->addr = addr;
-               __entry->res = res;
-       ),
-       TP_printk("[%s:%d] val=0x%08x", __get_str(name), __entry->addr, __entry->res)
-);
-
-TRACE_EVENT(hda_unsol_event,
-       TP_PROTO(struct hdac_bus *bus, u32 res, u32 res_ex),
-       TP_ARGS(bus, res, res_ex),
-       TP_STRUCT__entry(
-               __string(name, dev_name((bus)->dev))
-               __field(u32, res)
-               __field(u32, res_ex)
-       ),
-       TP_fast_assign(
-               __assign_str(name);
-               __entry->res = res;
-               __entry->res_ex = res_ex;
-       ),
-       TP_printk("[%s:%d] res=0x%08x, res_ex=0x%08x", __get_str(name),
-                 __entry->res_ex & 0x0f, __entry->res, __entry->res_ex)
-);
-
-DECLARE_EVENT_CLASS(hdac_stream,
-       TP_PROTO(struct hdac_bus *bus, struct hdac_stream *azx_dev),
-
-       TP_ARGS(bus, azx_dev),
-
-       TP_STRUCT__entry(
-               __field(unsigned char, stream_tag)
-       ),
-
-       TP_fast_assign(
-               __entry->stream_tag = (azx_dev)->stream_tag;
-       ),
-
-       TP_printk("stream_tag: %d", __entry->stream_tag)
-);
-
-DEFINE_EVENT(hdac_stream, snd_hdac_stream_start,
-       TP_PROTO(struct hdac_bus *bus, struct hdac_stream *azx_dev),
-       TP_ARGS(bus, azx_dev)
-);
-
-DEFINE_EVENT(hdac_stream, snd_hdac_stream_stop,
-       TP_PROTO(struct hdac_bus *bus, struct hdac_stream *azx_dev),
-       TP_ARGS(bus, azx_dev)
-);
-
-#endif /* __HDAC_TRACE_H */
-
-/* This part must be outside protection */
-#undef TRACE_INCLUDE_PATH
-#define TRACE_INCLUDE_PATH .
-
-#undef TRACE_INCLUDE_FILE
-#define TRACE_INCLUDE_FILE trace
-
-#include <trace/define_trace.h>