Merge tag 'asoc-v5.18' of https://git.kernel.org/pub/scm/linux/kernel/git/broonie...
authorTakashi Iwai <tiwai@suse.de>
Mon, 21 Mar 2022 15:19:21 +0000 (16:19 +0100)
committerTakashi Iwai <tiwai@suse.de>
Mon, 21 Mar 2022 15:19:21 +0000 (16:19 +0100)
ASoC: Updates for v5.18

Quite a quiet release for ASoC, lots of work on drivers and platforms
but nothing too groundbreaking but not much on the core itself:

 - Start of moving SoF to support multiple IPC mechanisms.
 - Use of NHLT ACPI table to reduce the amount of quirking required for
   Intel systems.
 - Some building blocks for use in forthcoming Intel AVS driver for
   legacy Intel DSP firmwares.
 - Support for AMD PDM, Atmel PDMC, Awinic AW8738, i.MX cards with
   TLV320AIC31xx, Intel machines with CS35L41 and ESSX8336, Mediatek
   MT8181 wideband bluetooth, nVidia Tegra234, Qualcomm SC7280, Renesas
   RZ/V2L, Texas Instruments TAS585M

91 files changed:
Documentation/devicetree/bindings/sound/google,cros-ec-codec.yaml
Documentation/devicetree/bindings/sound/nvidia,tegra30-hda.yaml
Documentation/sound/designs/control-names.rst
MAINTAINERS
drivers/acpi/scan.c
drivers/input/touchscreen/wm97xx-core.c
drivers/platform/x86/Kconfig
drivers/platform/x86/Makefile
drivers/platform/x86/i2c-multi-instantiate.c [deleted file]
drivers/platform/x86/serial-multi-instantiate.c [new file with mode: 0644]
drivers/soc/mediatek/mtk-scpsys.c
drivers/spi/spi.c
include/linux/spi/spi.h
include/sound/hda_codec.h
include/sound/hda_verbs.h
include/sound/hdaudio_ext.h
include/sound/pcm.h
include/uapi/sound/asound.h
sound/core/Kconfig
sound/core/memalloc.c
sound/core/oss/pcm_oss.c
sound/core/oss/pcm_plugin.c
sound/core/pcm_native.c
sound/core/seq/oss/seq_oss_init.c
sound/firewire/fcp.c
sound/hda/ext/hdac_ext_controller.c
sound/hda/hdac_i915.c
sound/hda/intel-sdw-acpi.c
sound/mips/snd-n64.c
sound/pci/ca0106/ca0106.h
sound/pci/ca0106/ca0106_main.c
sound/pci/ca0106/ca0106_mixer.c
sound/pci/cmipci.c
sound/pci/echoaudio/midi.c
sound/pci/hda/Kconfig
sound/pci/hda/hda_auto_parser.c
sound/pci/hda/hda_codec.c
sound/pci/hda/hda_generic.c
sound/pci/hda/hda_generic.h
sound/pci/hda/hda_intel.c
sound/pci/hda/hda_local.h
sound/pci/hda/hda_tegra.c
sound/pci/hda/patch_hdmi.c
sound/pci/hda/patch_realtek.c
sound/pci/lola/lola_mixer.c
sound/soc/amd/acp/acp-mach-common.c
sound/soc/amd/acp/acp-mach.h
sound/soc/amd/acp/acp-sof-mach.c
sound/soc/amd/vangogh/acp5x-i2s.c
sound/soc/amd/vangogh/acp5x-mach.c
sound/soc/amd/vangogh/acp5x.h
sound/soc/amd/vangogh/pci-acp5x.c
sound/soc/codecs/cs35l41.c
sound/soc/codecs/cs4265.c
sound/soc/codecs/hdac_hda.c
sound/soc/codecs/hdmi-codec.c
sound/soc/codecs/lpass-rx-macro.c
sound/soc/codecs/lpass-va-macro.c
sound/soc/codecs/nau8821.c
sound/soc/codecs/rt5668.c
sound/soc/codecs/rt5682-i2c.c
sound/soc/codecs/rt5682.c
sound/soc/codecs/rt5682.h
sound/soc/codecs/rt5682s.c
sound/soc/codecs/rt5682s.h
sound/soc/codecs/tas2770.c
sound/soc/codecs/tlv320adc3xxx.c
sound/soc/codecs/wcd934x.c
sound/soc/codecs/wcd938x.c
sound/soc/codecs/wm_adsp.c
sound/soc/fsl/pcm030-audio-fabric.c
sound/soc/intel/common/soc-acpi-intel-byt-match.c
sound/soc/intel/skylake/skl.c
sound/soc/qcom/lpass-platform.c
sound/soc/qcom/qdsp6/q6apm-dai.c
sound/soc/soc-acpi.c
sound/soc/soc-ops.c
sound/soc/soc-pcm.c
sound/soc/sof/intel/hda-loader.c
sound/soc/sof/intel/hda.c
sound/sound_core.c
sound/spi/at73c213.c
sound/usb/implicit.c
sound/usb/mixer.c
sound/usb/mixer_maps.c
sound/usb/mixer_quirks.c
sound/usb/mixer_s1810c.c
sound/usb/mixer_scarlett_gen2.c
sound/usb/quirks-table.h
sound/x86/intel_hdmi_audio.c
tools/testing/selftests/alsa/mixer-test.c

index 77adbeb..c3e9f34 100644 (file)
@@ -8,6 +8,7 @@ title: Audio codec controlled by ChromeOS EC
 
 maintainers:
   - Cheng-Yi Chiang <cychiang@chromium.org>
+  - Tzung-Bi Shih <tzungbi@google.com>
 
 description: |
   Google's ChromeOS EC codec is a digital mic codec provided by the
index 2c913aa..12c31b4 100644 (file)
@@ -23,6 +23,7 @@ properties:
       - const: nvidia,tegra30-hda
       - items:
           - enum:
+              - nvidia,tegra234-hda
               - nvidia,tegra194-hda
               - nvidia,tegra186-hda
               - nvidia,tegra210-hda
@@ -41,9 +42,11 @@ properties:
     maxItems: 1
 
   clocks:
+    minItems: 2
     maxItems: 3
 
   clock-names:
+    minItems: 2
     items:
       - const: hda
       - const: hda2hdmi
index 7fedd0f..765ff9b 100644 (file)
@@ -34,7 +34,7 @@ CHANNEL
 Front          front left/right channels
 Surround       rear left/right in 4.0/5.1 surround
 CLFE           C/LFE channels
-Center         center cannel
+Center         center channel
 LFE            LFE channel
 Side           side left/right for 7.1 surround
 ============   ==================================================
index 19625fe..e37e8cb 100644 (file)
@@ -388,11 +388,11 @@ L:        linux-arm-kernel@lists.infradead.org (moderated for non-subscribers)
 S:     Maintained
 F:     drivers/acpi/arm64
 
-ACPI I2C MULTI INSTANTIATE DRIVER
+ACPI SERIAL MULTI INSTANTIATE DRIVER
 M:     Hans de Goede <hdegoede@redhat.com>
 L:     platform-driver-x86@vger.kernel.org
 S:     Maintained
-F:     drivers/platform/x86/i2c-multi-instantiate.c
+F:     drivers/platform/x86/serial-multi-instantiate.c
 
 ACPI PCC(Platform Communication Channel) MAILBOX DRIVER
 M:     Sudeep Holla <sudeep.holla@arm.com>
@@ -4543,6 +4543,7 @@ F:        drivers/platform/chrome/
 
 CHROMEOS EC CODEC DRIVER
 M:     Cheng-Yi Chiang <cychiang@chromium.org>
+M:     Tzung-Bi Shih <tzungbi@google.com>
 R:     Guenter Roeck <groeck@chromium.org>
 S:     Maintained
 F:     Documentation/devicetree/bindings/sound/google,cros-ec-codec.yaml
index 1331756..4463c2e 100644 (file)
@@ -1734,17 +1734,21 @@ static bool acpi_device_enumeration_by_parent(struct acpi_device *device)
        bool is_serial_bus_slave = false;
        static const struct acpi_device_id ignore_serial_bus_ids[] = {
        /*
-        * These devices have multiple I2cSerialBus resources and an i2c-client
-        * must be instantiated for each, each with its own i2c_device_id.
-        * Normally we only instantiate an i2c-client for the first resource,
-        * using the ACPI HID as id. These special cases are handled by the
-        * drivers/platform/x86/i2c-multi-instantiate.c driver, which knows
-        * which i2c_device_id to use for each resource.
+        * These devices have multiple SerialBus resources and a client
+        * device must be instantiated for each of them, each with
+        * its own device id.
+        * Normally we only instantiate one client device for the first
+        * resource, using the ACPI HID as id. These special cases are handled
+        * by the drivers/platform/x86/serial-multi-instantiate.c driver, which
+        * knows which client device id to use for each resource.
         */
                {"BSG1160", },
                {"BSG2150", },
+               {"CSC3551", },
                {"INT33FE", },
                {"INT3515", },
+               /* Non-conforming _HID for Cirrus Logic already released */
+               {"CLSA0100", },
        /*
         * HIDs of device with an UartSerialBusV2 resource for which userspace
         * expects a regular tty cdev to be created (instead of the in kernel
index 78d2ee9..1b58611 100644 (file)
@@ -615,10 +615,9 @@ static int wm97xx_register_touch(struct wm97xx *wm)
         * extensions)
         */
        wm->touch_dev = platform_device_alloc("wm97xx-touch", -1);
-       if (!wm->touch_dev) {
-               ret = -ENOMEM;
-               goto touch_err;
-       }
+       if (!wm->touch_dev)
+               return -ENOMEM;
+
        platform_set_drvdata(wm->touch_dev, wm);
        wm->touch_dev->dev.parent = wm->dev;
        wm->touch_dev->dev.platform_data = pdata;
@@ -629,9 +628,6 @@ static int wm97xx_register_touch(struct wm97xx *wm)
        return 0;
 touch_reg_err:
        platform_device_put(wm->touch_dev);
-touch_err:
-       input_unregister_device(wm->input_dev);
-       wm->input_dev = NULL;
 
        return ret;
 }
@@ -639,8 +635,6 @@ touch_err:
 static void wm97xx_unregister_touch(struct wm97xx *wm)
 {
        platform_device_unregister(wm->touch_dev);
-       input_unregister_device(wm->input_dev);
-       wm->input_dev = NULL;
 }
 
 static int _wm97xx_probe(struct wm97xx *wm)
index 24deeeb..8d1eec2 100644 (file)
@@ -990,16 +990,16 @@ config TOPSTAR_LAPTOP
 
          If you have a Topstar laptop, say Y or M here.
 
-config I2C_MULTI_INSTANTIATE
-       tristate "I2C multi instantiate pseudo device driver"
-       depends on I2C && ACPI
+config SERIAL_MULTI_INSTANTIATE
+       tristate "Serial bus multi instantiate pseudo device driver"
+       depends on I2C && SPI && ACPI
        help
-         Some ACPI-based systems list multiple i2c-devices in a single ACPI
-         firmware-node. This driver will instantiate separate i2c-clients
+         Some ACPI-based systems list multiple devices in a single ACPI
+         firmware-node. This driver will instantiate separate clients
          for each device in the firmware-node.
 
          To compile this driver as a module, choose M here: the module
-         will be called i2c-multi-instantiate.
+         will be called serial-multi-instantiate.
 
 config MLX_PLATFORM
        tristate "Mellanox Technologies platform support"
index c12a9b0..9527088 100644 (file)
@@ -110,7 +110,7 @@ obj-$(CONFIG_TOPSTAR_LAPTOP)        += topstar-laptop.o
 
 # Platform drivers
 obj-$(CONFIG_FW_ATTR_CLASS)            += firmware_attributes_class.o
-obj-$(CONFIG_I2C_MULTI_INSTANTIATE)    += i2c-multi-instantiate.o
+obj-$(CONFIG_SERIAL_MULTI_INSTANTIATE) += serial-multi-instantiate.o
 obj-$(CONFIG_MLX_PLATFORM)             += mlx-platform.o
 obj-$(CONFIG_TOUCHSCREEN_DMI)          += touchscreen_dmi.o
 obj-$(CONFIG_WIRELESS_HOTKEY)          += wireless-hotkey.o
diff --git a/drivers/platform/x86/i2c-multi-instantiate.c b/drivers/platform/x86/i2c-multi-instantiate.c
deleted file mode 100644 (file)
index 4956a1d..0000000
+++ /dev/null
@@ -1,174 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0+
-/*
- * I2C multi-instantiate driver, pseudo driver to instantiate multiple
- * i2c-clients from a single fwnode.
- *
- * Copyright 2018 Hans de Goede <hdegoede@redhat.com>
- */
-
-#include <linux/acpi.h>
-#include <linux/bits.h>
-#include <linux/i2c.h>
-#include <linux/interrupt.h>
-#include <linux/kernel.h>
-#include <linux/module.h>
-#include <linux/platform_device.h>
-#include <linux/property.h>
-#include <linux/types.h>
-
-#define IRQ_RESOURCE_TYPE      GENMASK(1, 0)
-#define IRQ_RESOURCE_NONE      0
-#define IRQ_RESOURCE_GPIO      1
-#define IRQ_RESOURCE_APIC      2
-
-struct i2c_inst_data {
-       const char *type;
-       unsigned int flags;
-       int irq_idx;
-};
-
-struct i2c_multi_inst_data {
-       int num_clients;
-       struct i2c_client *clients[];
-};
-
-static int i2c_multi_inst_probe(struct platform_device *pdev)
-{
-       struct i2c_multi_inst_data *multi;
-       const struct i2c_inst_data *inst_data;
-       struct i2c_board_info board_info = {};
-       struct device *dev = &pdev->dev;
-       struct acpi_device *adev;
-       char name[32];
-       int i, ret;
-
-       inst_data = device_get_match_data(dev);
-       if (!inst_data) {
-               dev_err(dev, "Error ACPI match data is missing\n");
-               return -ENODEV;
-       }
-
-       adev = ACPI_COMPANION(dev);
-
-       /* Count number of clients to instantiate */
-       ret = i2c_acpi_client_count(adev);
-       if (ret < 0)
-               return ret;
-
-       multi = devm_kmalloc(dev, struct_size(multi, clients, ret), GFP_KERNEL);
-       if (!multi)
-               return -ENOMEM;
-
-       multi->num_clients = ret;
-
-       for (i = 0; i < multi->num_clients && inst_data[i].type; i++) {
-               memset(&board_info, 0, sizeof(board_info));
-               strlcpy(board_info.type, inst_data[i].type, I2C_NAME_SIZE);
-               snprintf(name, sizeof(name), "%s-%s.%d", dev_name(dev),
-                        inst_data[i].type, i);
-               board_info.dev_name = name;
-               switch (inst_data[i].flags & IRQ_RESOURCE_TYPE) {
-               case IRQ_RESOURCE_GPIO:
-                       ret = acpi_dev_gpio_irq_get(adev, inst_data[i].irq_idx);
-                       if (ret < 0) {
-                               dev_err(dev, "Error requesting irq at index %d: %d\n",
-                                       inst_data[i].irq_idx, ret);
-                               goto error;
-                       }
-                       board_info.irq = ret;
-                       break;
-               case IRQ_RESOURCE_APIC:
-                       ret = platform_get_irq(pdev, inst_data[i].irq_idx);
-                       if (ret < 0) {
-                               dev_dbg(dev, "Error requesting irq at index %d: %d\n",
-                                       inst_data[i].irq_idx, ret);
-                               goto error;
-                       }
-                       board_info.irq = ret;
-                       break;
-               default:
-                       board_info.irq = 0;
-                       break;
-               }
-               multi->clients[i] = i2c_acpi_new_device(dev, i, &board_info);
-               if (IS_ERR(multi->clients[i])) {
-                       ret = dev_err_probe(dev, PTR_ERR(multi->clients[i]),
-                                           "Error creating i2c-client, idx %d\n", i);
-                       goto error;
-               }
-       }
-       if (i < multi->num_clients) {
-               dev_err(dev, "Error finding driver, idx %d\n", i);
-               ret = -ENODEV;
-               goto error;
-       }
-
-       platform_set_drvdata(pdev, multi);
-       return 0;
-
-error:
-       while (--i >= 0)
-               i2c_unregister_device(multi->clients[i]);
-
-       return ret;
-}
-
-static int i2c_multi_inst_remove(struct platform_device *pdev)
-{
-       struct i2c_multi_inst_data *multi = platform_get_drvdata(pdev);
-       int i;
-
-       for (i = 0; i < multi->num_clients; i++)
-               i2c_unregister_device(multi->clients[i]);
-
-       return 0;
-}
-
-static const struct i2c_inst_data bsg1160_data[]  = {
-       { "bmc150_accel", IRQ_RESOURCE_GPIO, 0 },
-       { "bmc150_magn" },
-       { "bmg160" },
-       {}
-};
-
-static const struct i2c_inst_data bsg2150_data[]  = {
-       { "bmc150_accel", IRQ_RESOURCE_GPIO, 0 },
-       { "bmc150_magn" },
-       /* The resources describe a 3th client, but it is not really there. */
-       { "bsg2150_dummy_dev" },
-       {}
-};
-
-static const struct i2c_inst_data int3515_data[]  = {
-       { "tps6598x", IRQ_RESOURCE_APIC, 0 },
-       { "tps6598x", IRQ_RESOURCE_APIC, 1 },
-       { "tps6598x", IRQ_RESOURCE_APIC, 2 },
-       { "tps6598x", IRQ_RESOURCE_APIC, 3 },
-       {}
-};
-
-/*
- * Note new device-ids must also be added to i2c_multi_instantiate_ids in
- * drivers/acpi/scan.c: acpi_device_enumeration_by_parent().
- */
-static const struct acpi_device_id i2c_multi_inst_acpi_ids[] = {
-       { "BSG1160", (unsigned long)bsg1160_data },
-       { "BSG2150", (unsigned long)bsg2150_data },
-       { "INT3515", (unsigned long)int3515_data },
-       { }
-};
-MODULE_DEVICE_TABLE(acpi, i2c_multi_inst_acpi_ids);
-
-static struct platform_driver i2c_multi_inst_driver = {
-       .driver = {
-               .name = "I2C multi instantiate pseudo device driver",
-               .acpi_match_table = i2c_multi_inst_acpi_ids,
-       },
-       .probe = i2c_multi_inst_probe,
-       .remove = i2c_multi_inst_remove,
-};
-module_platform_driver(i2c_multi_inst_driver);
-
-MODULE_DESCRIPTION("I2C multi instantiate pseudo device driver");
-MODULE_AUTHOR("Hans de Goede <hdegoede@redhat.com>");
-MODULE_LICENSE("GPL");
diff --git a/drivers/platform/x86/serial-multi-instantiate.c b/drivers/platform/x86/serial-multi-instantiate.c
new file mode 100644 (file)
index 0000000..1e8063b
--- /dev/null
@@ -0,0 +1,348 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Serial multi-instantiate driver, pseudo driver to instantiate multiple
+ * client devices from a single fwnode.
+ *
+ * Copyright 2018 Hans de Goede <hdegoede@redhat.com>
+ */
+
+#include <linux/acpi.h>
+#include <linux/bits.h>
+#include <linux/i2c.h>
+#include <linux/interrupt.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/property.h>
+#include <linux/spi/spi.h>
+#include <linux/types.h>
+
+#define IRQ_RESOURCE_TYPE      GENMASK(1, 0)
+#define IRQ_RESOURCE_NONE      0
+#define IRQ_RESOURCE_GPIO      1
+#define IRQ_RESOURCE_APIC      2
+
+enum smi_bus_type {
+       SMI_I2C,
+       SMI_SPI,
+       SMI_AUTO_DETECT,
+};
+
+struct smi_instance {
+       const char *type;
+       unsigned int flags;
+       int irq_idx;
+};
+
+struct smi_node {
+       enum smi_bus_type bus_type;
+       struct smi_instance instances[];
+};
+
+struct smi {
+       int i2c_num;
+       int spi_num;
+       struct i2c_client **i2c_devs;
+       struct spi_device **spi_devs;
+};
+
+static int smi_get_irq(struct platform_device *pdev, struct acpi_device *adev,
+                      const struct smi_instance *inst)
+{
+       int ret;
+
+       switch (inst->flags & IRQ_RESOURCE_TYPE) {
+       case IRQ_RESOURCE_GPIO:
+               ret = acpi_dev_gpio_irq_get(adev, inst->irq_idx);
+               break;
+       case IRQ_RESOURCE_APIC:
+               ret = platform_get_irq(pdev, inst->irq_idx);
+               break;
+       default:
+               return 0;
+       }
+
+       if (ret < 0)
+               dev_err_probe(&pdev->dev, ret, "Error requesting irq at index %d: %d\n",
+                             inst->irq_idx, ret);
+
+       return ret;
+}
+
+static void smi_devs_unregister(struct smi *smi)
+{
+       while (smi->i2c_num > 0)
+               i2c_unregister_device(smi->i2c_devs[--smi->i2c_num]);
+
+       while (smi->spi_num > 0)
+               spi_unregister_device(smi->spi_devs[--smi->spi_num]);
+}
+
+/**
+ * smi_spi_probe - Instantiate multiple SPI devices from inst array
+ * @pdev:      Platform device
+ * @adev:      ACPI device
+ * @smi:       Internal struct for Serial multi instantiate driver
+ * @inst_array:        Array of instances to probe
+ *
+ * Returns the number of SPI devices instantiate, Zero if none is found or a negative error code.
+ */
+static int smi_spi_probe(struct platform_device *pdev, struct acpi_device *adev, struct smi *smi,
+                        const struct smi_instance *inst_array)
+{
+       struct device *dev = &pdev->dev;
+       struct spi_controller *ctlr;
+       struct spi_device *spi_dev;
+       char name[50];
+       int i, ret, count;
+
+       ret = acpi_spi_count_resources(adev);
+       if (ret < 0)
+               return ret;
+       else if (!ret)
+               return -ENODEV;
+
+       count = ret;
+
+       smi->spi_devs = devm_kcalloc(dev, count, sizeof(*smi->spi_devs), GFP_KERNEL);
+       if (!smi->spi_devs)
+               return -ENOMEM;
+
+       for (i = 0; i < count && inst_array[i].type; i++) {
+
+               spi_dev = acpi_spi_device_alloc(NULL, adev, i);
+               if (IS_ERR(spi_dev)) {
+                       ret = PTR_ERR(spi_dev);
+                       dev_err_probe(dev, ret, "failed to allocate SPI device %s from ACPI: %d\n",
+                                     dev_name(&adev->dev), ret);
+                       goto error;
+               }
+
+               ctlr = spi_dev->controller;
+
+               strscpy(spi_dev->modalias, inst_array[i].type, sizeof(spi_dev->modalias));
+
+               ret = smi_get_irq(pdev, adev, &inst_array[i]);
+               if (ret < 0) {
+                       spi_dev_put(spi_dev);
+                       goto error;
+               }
+               spi_dev->irq = ret;
+
+               snprintf(name, sizeof(name), "%s-%s-%s.%d", dev_name(&ctlr->dev), dev_name(dev),
+                        inst_array[i].type, i);
+               spi_dev->dev.init_name = name;
+
+               ret = spi_add_device(spi_dev);
+               if (ret) {
+                       dev_err_probe(&ctlr->dev, ret,
+                                     "failed to add SPI device %s from ACPI: %d\n",
+                                     dev_name(&adev->dev), ret);
+                       spi_dev_put(spi_dev);
+                       goto error;
+               }
+
+               dev_dbg(dev, "SPI device %s using chip select %u", name, spi_dev->chip_select);
+
+               smi->spi_devs[i] = spi_dev;
+               smi->spi_num++;
+       }
+
+       if (smi->spi_num < count) {
+               dev_dbg(dev, "Error finding driver, idx %d\n", i);
+               ret = -ENODEV;
+               goto error;
+       }
+
+       dev_info(dev, "Instantiated %d SPI devices.\n", smi->spi_num);
+
+       return 0;
+error:
+       smi_devs_unregister(smi);
+
+       return ret;
+}
+
+/**
+ * smi_i2c_probe - Instantiate multiple I2C devices from inst array
+ * @pdev:      Platform device
+ * @adev:      ACPI device
+ * @smi:       Internal struct for Serial multi instantiate driver
+ * @inst_array:        Array of instances to probe
+ *
+ * Returns the number of I2C devices instantiate, Zero if none is found or a negative error code.
+ */
+static int smi_i2c_probe(struct platform_device *pdev, struct acpi_device *adev, struct smi *smi,
+                        const struct smi_instance *inst_array)
+{
+       struct i2c_board_info board_info = {};
+       struct device *dev = &pdev->dev;
+       char name[32];
+       int i, ret, count;
+
+       ret = i2c_acpi_client_count(adev);
+       if (ret < 0)
+               return ret;
+       else if (!ret)
+               return -ENODEV;
+
+       count = ret;
+
+       smi->i2c_devs = devm_kcalloc(dev, count, sizeof(*smi->i2c_devs), GFP_KERNEL);
+       if (!smi->i2c_devs)
+               return -ENOMEM;
+
+       for (i = 0; i < count && inst_array[i].type; i++) {
+               memset(&board_info, 0, sizeof(board_info));
+               strscpy(board_info.type, inst_array[i].type, I2C_NAME_SIZE);
+               snprintf(name, sizeof(name), "%s-%s.%d", dev_name(dev), inst_array[i].type, i);
+               board_info.dev_name = name;
+
+               ret = smi_get_irq(pdev, adev, &inst_array[i]);
+               if (ret < 0)
+                       goto error;
+               board_info.irq = ret;
+
+               smi->i2c_devs[i] = i2c_acpi_new_device(dev, i, &board_info);
+               if (IS_ERR(smi->i2c_devs[i])) {
+                       ret = dev_err_probe(dev, PTR_ERR(smi->i2c_devs[i]),
+                                           "Error creating i2c-client, idx %d\n", i);
+                       goto error;
+               }
+               smi->i2c_num++;
+       }
+       if (smi->i2c_num < count) {
+               dev_dbg(dev, "Error finding driver, idx %d\n", i);
+               ret = -ENODEV;
+               goto error;
+       }
+
+       dev_info(dev, "Instantiated %d I2C devices.\n", smi->i2c_num);
+
+       return 0;
+error:
+       smi_devs_unregister(smi);
+
+       return ret;
+}
+
+static int smi_probe(struct platform_device *pdev)
+{
+       struct device *dev = &pdev->dev;
+       const struct smi_node *node;
+       struct acpi_device *adev;
+       struct smi *smi;
+
+       adev = ACPI_COMPANION(dev);
+       if (!adev)
+               return -ENODEV;
+
+       node = device_get_match_data(dev);
+       if (!node) {
+               dev_dbg(dev, "Error ACPI match data is missing\n");
+               return -ENODEV;
+       }
+
+       smi = devm_kzalloc(dev, sizeof(*smi), GFP_KERNEL);
+       if (!smi)
+               return -ENOMEM;
+
+       platform_set_drvdata(pdev, smi);
+
+       switch (node->bus_type) {
+       case SMI_I2C:
+               return smi_i2c_probe(pdev, adev, smi, node->instances);
+       case SMI_SPI:
+               return smi_spi_probe(pdev, adev, smi, node->instances);
+       case SMI_AUTO_DETECT:
+               if (i2c_acpi_client_count(adev) > 0)
+                       return smi_i2c_probe(pdev, adev, smi, node->instances);
+               else
+                       return smi_spi_probe(pdev, adev, smi, node->instances);
+       default:
+               return -EINVAL;
+       }
+
+       return 0; /* never reached */
+}
+
+static int smi_remove(struct platform_device *pdev)
+{
+       struct smi *smi = platform_get_drvdata(pdev);
+
+       smi_devs_unregister(smi);
+
+       return 0;
+}
+
+static const struct smi_node bsg1160_data = {
+       .instances = {
+               { "bmc150_accel", IRQ_RESOURCE_GPIO, 0 },
+               { "bmc150_magn" },
+               { "bmg160" },
+               {}
+       },
+       .bus_type = SMI_I2C,
+};
+
+static const struct smi_node bsg2150_data = {
+       .instances = {
+               { "bmc150_accel", IRQ_RESOURCE_GPIO, 0 },
+               { "bmc150_magn" },
+               /* The resources describe a 3th client, but it is not really there. */
+               { "bsg2150_dummy_dev" },
+               {}
+       },
+       .bus_type = SMI_I2C,
+};
+
+static const struct smi_node int3515_data = {
+       .instances = {
+               { "tps6598x", IRQ_RESOURCE_APIC, 0 },
+               { "tps6598x", IRQ_RESOURCE_APIC, 1 },
+               { "tps6598x", IRQ_RESOURCE_APIC, 2 },
+               { "tps6598x", IRQ_RESOURCE_APIC, 3 },
+               {}
+       },
+       .bus_type = SMI_I2C,
+};
+
+static const struct smi_node cs35l41_hda = {
+       .instances = {
+               { "cs35l41-hda", IRQ_RESOURCE_GPIO, 0 },
+               { "cs35l41-hda", IRQ_RESOURCE_GPIO, 0 },
+               { "cs35l41-hda", IRQ_RESOURCE_GPIO, 0 },
+               { "cs35l41-hda", IRQ_RESOURCE_GPIO, 0 },
+               {}
+       },
+       .bus_type = SMI_AUTO_DETECT,
+};
+
+/*
+ * Note new device-ids must also be added to ignore_serial_bus_ids in
+ * drivers/acpi/scan.c: acpi_device_enumeration_by_parent().
+ */
+static const struct acpi_device_id smi_acpi_ids[] = {
+       { "BSG1160", (unsigned long)&bsg1160_data },
+       { "BSG2150", (unsigned long)&bsg2150_data },
+       { "INT3515", (unsigned long)&int3515_data },
+       { "CSC3551", (unsigned long)&cs35l41_hda },
+       /* Non-conforming _HID for Cirrus Logic already released */
+       { "CLSA0100", (unsigned long)&cs35l41_hda },
+       { }
+};
+MODULE_DEVICE_TABLE(acpi, smi_acpi_ids);
+
+static struct platform_driver smi_driver = {
+       .driver = {
+               .name = "Serial bus multi instantiate pseudo device driver",
+               .acpi_match_table = smi_acpi_ids,
+       },
+       .probe = smi_probe,
+       .remove = smi_remove,
+};
+module_platform_driver(smi_driver);
+
+MODULE_DESCRIPTION("Serial multi instantiate pseudo device driver");
+MODULE_AUTHOR("Hans de Goede <hdegoede@redhat.com>");
+MODULE_LICENSE("GPL");
index 670cc82..ca75b14 100644 (file)
@@ -411,17 +411,12 @@ out:
        return ret;
 }
 
-static int init_clks(struct platform_device *pdev, struct clk **clk)
+static void init_clks(struct platform_device *pdev, struct clk **clk)
 {
        int i;
 
-       for (i = CLK_NONE + 1; i < CLK_MAX; i++) {
+       for (i = CLK_NONE + 1; i < CLK_MAX; i++)
                clk[i] = devm_clk_get(&pdev->dev, clk_names[i]);
-               if (IS_ERR(clk[i]))
-                       return PTR_ERR(clk[i]);
-       }
-
-       return 0;
 }
 
 static struct scp *init_scp(struct platform_device *pdev,
@@ -431,7 +426,7 @@ static struct scp *init_scp(struct platform_device *pdev,
 {
        struct genpd_onecell_data *pd_data;
        struct resource *res;
-       int i, j, ret;
+       int i, j;
        struct scp *scp;
        struct clk *clk[CLK_MAX];
 
@@ -486,9 +481,7 @@ static struct scp *init_scp(struct platform_device *pdev,
 
        pd_data->num_domains = num;
 
-       ret = init_clks(pdev, clk);
-       if (ret)
-               return ERR_PTR(ret);
+       init_clks(pdev, clk);
 
        for (i = 0; i < num; i++) {
                struct scp_domain *scpd = &scp->domains[i];
index 4599b12..ec9f2ed 100644 (file)
@@ -532,7 +532,7 @@ static DEFINE_MUTEX(board_lock);
  *
  * Return: a pointer to the new device, or NULL.
  */
-static struct spi_device *spi_alloc_device(struct spi_controller *ctlr)
+struct spi_device *spi_alloc_device(struct spi_controller *ctlr)
 {
        struct spi_device       *spi;
 
@@ -557,6 +557,7 @@ static struct spi_device *spi_alloc_device(struct spi_controller *ctlr)
        device_initialize(&spi->dev);
        return spi;
 }
+EXPORT_SYMBOL_GPL(spi_alloc_device);
 
 static void spi_dev_set_name(struct spi_device *spi)
 {
@@ -652,7 +653,7 @@ static int __spi_add_device(struct spi_device *spi)
  *
  * Return: 0 on success; negative errno on failure
  */
-static int spi_add_device(struct spi_device *spi)
+int spi_add_device(struct spi_device *spi)
 {
        struct spi_controller *ctlr = spi->controller;
        struct device *dev = ctlr->dev.parent;
@@ -673,6 +674,7 @@ static int spi_add_device(struct spi_device *spi)
        mutex_unlock(&ctlr->add_lock);
        return status;
 }
+EXPORT_SYMBOL_GPL(spi_add_device);
 
 static int spi_add_device_locked(struct spi_device *spi)
 {
@@ -2318,8 +2320,50 @@ struct acpi_spi_lookup {
        int                     irq;
        u8                      bits_per_word;
        u8                      chip_select;
+       int                     n;
+       int                     index;
 };
 
+static int acpi_spi_count(struct acpi_resource *ares, void *data)
+{
+       struct acpi_resource_spi_serialbus *sb;
+       int *count = data;
+
+       if (ares->type != ACPI_RESOURCE_TYPE_SERIAL_BUS)
+               return 1;
+
+       sb = &ares->data.spi_serial_bus;
+       if (sb->type != ACPI_RESOURCE_SERIAL_TYPE_SPI)
+               return 1;
+
+       *count = *count + 1;
+
+       return 1;
+}
+
+/**
+ * acpi_spi_count_resources - Count the number of SpiSerialBus resources
+ * @adev:      ACPI device
+ *
+ * Returns the number of SpiSerialBus resources in the ACPI-device's
+ * resource-list; or a negative error code.
+ */
+int acpi_spi_count_resources(struct acpi_device *adev)
+{
+       LIST_HEAD(r);
+       int count = 0;
+       int ret;
+
+       ret = acpi_dev_get_resources(adev, &r, acpi_spi_count, &count);
+       if (ret < 0)
+               return ret;
+
+       acpi_dev_free_resource_list(&r);
+
+       return count;
+}
+EXPORT_SYMBOL_GPL(acpi_spi_count_resources);
+
 static void acpi_spi_parse_apple_properties(struct acpi_device *dev,
                                            struct acpi_spi_lookup *lookup)
 {
@@ -2349,6 +2393,8 @@ static void acpi_spi_parse_apple_properties(struct acpi_device *dev,
                lookup->mode |= SPI_CPHA;
 }
 
+static struct spi_controller *acpi_spi_find_controller_by_adev(struct acpi_device *adev);
+
 static int acpi_spi_add_resource(struct acpi_resource *ares, void *data)
 {
        struct acpi_spi_lookup *lookup = data;
@@ -2362,14 +2408,35 @@ static int acpi_spi_add_resource(struct acpi_resource *ares, void *data)
                sb = &ares->data.spi_serial_bus;
                if (sb->type == ACPI_RESOURCE_SERIAL_TYPE_SPI) {
 
+                       if (lookup->index != -1 && lookup->n++ != lookup->index)
+                               return 1;
+
+                       if (lookup->index == -1 && !ctlr)
+                               return -ENODEV;
+
                        status = acpi_get_handle(NULL,
                                                 sb->resource_source.string_ptr,
                                                 &parent_handle);
 
-                       if (ACPI_FAILURE(status) ||
-                           ACPI_HANDLE(ctlr->dev.parent) != parent_handle)
+                       if (ACPI_FAILURE(status))
                                return -ENODEV;
 
+                       if (ctlr) {
+                               if (ACPI_HANDLE(ctlr->dev.parent) != parent_handle)
+                                       return -ENODEV;
+                       } else {
+                               struct acpi_device *adev;
+
+                               if (acpi_bus_get_device(parent_handle, &adev))
+                                       return -ENODEV;
+
+                               ctlr = acpi_spi_find_controller_by_adev(adev);
+                               if (!ctlr)
+                                       return -ENODEV;
+
+                               lookup->ctlr = ctlr;
+                       }
+
                        /*
                         * ACPI DeviceSelection numbering is handled by the
                         * host controller driver in Windows and can vary
@@ -2408,8 +2475,25 @@ static int acpi_spi_add_resource(struct acpi_resource *ares, void *data)
        return 1;
 }
 
-static acpi_status acpi_register_spi_device(struct spi_controller *ctlr,
-                                           struct acpi_device *adev)
+/**
+ * acpi_spi_device_alloc - Allocate a spi device, and fill it in with ACPI information
+ * @ctlr: controller to which the spi device belongs
+ * @adev: ACPI Device for the spi device
+ * @index: Index of the spi resource inside the ACPI Node
+ *
+ * This should be used to allocate a new spi device from and ACPI Node.
+ * The caller is responsible for calling spi_add_device to register the spi device.
+ *
+ * If ctlr is set to NULL, the Controller for the spi device will be looked up
+ * using the resource.
+ * If index is set to -1, index is not used.
+ * Note: If index is -1, ctlr must be set.
+ *
+ * Return: a pointer to the new device, or ERR_PTR on error.
+ */
+struct spi_device *acpi_spi_device_alloc(struct spi_controller *ctlr,
+                                        struct acpi_device *adev,
+                                        int index)
 {
        acpi_handle parent_handle = NULL;
        struct list_head resource_list;
@@ -2417,12 +2501,13 @@ static acpi_status acpi_register_spi_device(struct spi_controller *ctlr,
        struct spi_device *spi;
        int ret;
 
-       if (acpi_bus_get_status(adev) || !adev->status.present ||
-           acpi_device_enumerated(adev))
-               return AE_OK;
+       if (!ctlr && index == -1)
+               return ERR_PTR(-EINVAL);
 
        lookup.ctlr             = ctlr;
        lookup.irq              = -1;
+       lookup.index            = index;
+       lookup.n                = 0;
 
        INIT_LIST_HEAD(&resource_list);
        ret = acpi_dev_get_resources(adev, &resource_list,
@@ -2431,26 +2516,25 @@ static acpi_status acpi_register_spi_device(struct spi_controller *ctlr,
 
        if (ret < 0)
                /* found SPI in _CRS but it points to another controller */
-               return AE_OK;
+               return ERR_PTR(-ENODEV);
 
        if (!lookup.max_speed_hz &&
            ACPI_SUCCESS(acpi_get_parent(adev->handle, &parent_handle)) &&
-           ACPI_HANDLE(ctlr->dev.parent) == parent_handle) {
+           ACPI_HANDLE(lookup.ctlr->dev.parent) == parent_handle) {
                /* Apple does not use _CRS but nested devices for SPI slaves */
                acpi_spi_parse_apple_properties(adev, &lookup);
        }
 
        if (!lookup.max_speed_hz)
-               return AE_OK;
+               return ERR_PTR(-ENODEV);
 
-       spi = spi_alloc_device(ctlr);
+       spi = spi_alloc_device(lookup.ctlr);
        if (!spi) {
-               dev_err(&ctlr->dev, "failed to allocate SPI device for %s\n",
+               dev_err(&lookup.ctlr->dev, "failed to allocate SPI device for %s\n",
                        dev_name(&adev->dev));
-               return AE_NO_MEMORY;
+               return ERR_PTR(-ENOMEM);
        }
 
-
        ACPI_COMPANION_SET(&spi->dev, adev);
        spi->max_speed_hz       = lookup.max_speed_hz;
        spi->mode               |= lookup.mode;
@@ -2458,6 +2542,27 @@ static acpi_status acpi_register_spi_device(struct spi_controller *ctlr,
        spi->bits_per_word      = lookup.bits_per_word;
        spi->chip_select        = lookup.chip_select;
 
+       return spi;
+}
+EXPORT_SYMBOL_GPL(acpi_spi_device_alloc);
+
+static acpi_status acpi_register_spi_device(struct spi_controller *ctlr,
+                                           struct acpi_device *adev)
+{
+       struct spi_device *spi;
+
+       if (acpi_bus_get_status(adev) || !adev->status.present ||
+           acpi_device_enumerated(adev))
+               return AE_OK;
+
+       spi = acpi_spi_device_alloc(ctlr, adev, -1);
+       if (IS_ERR(spi)) {
+               if (PTR_ERR(spi) == -ENOMEM)
+                       return AE_NO_MEMORY;
+               else
+                       return AE_OK;
+       }
+
        acpi_set_modalias(adev, acpi_device_hid(adev), spi->modalias,
                          sizeof(spi->modalias));
 
index 7ab3fed..394b424 100644 (file)
@@ -16,6 +16,7 @@
 #include <linux/gpio/consumer.h>
 
 #include <uapi/linux/spi/spi.h>
+#include <linux/acpi.h>
 
 struct dma_chan;
 struct software_node;
@@ -759,6 +760,13 @@ extern int devm_spi_register_controller(struct device *dev,
                                        struct spi_controller *ctlr);
 extern void spi_unregister_controller(struct spi_controller *ctlr);
 
+#if IS_ENABLED(CONFIG_ACPI)
+extern struct spi_device *acpi_spi_device_alloc(struct spi_controller *ctlr,
+                                               struct acpi_device *adev,
+                                               int index);
+int acpi_spi_count_resources(struct acpi_device *adev);
+#endif
+
 /*
  * SPI resource management while processing a SPI message
  */
@@ -1452,7 +1460,19 @@ spi_register_board_info(struct spi_board_info const *info, unsigned n)
  * use spi_new_device() to describe each device.  You can also call
  * spi_unregister_device() to start making that device vanish, but
  * normally that would be handled by spi_unregister_controller().
+ *
+ * You can also use spi_alloc_device() and spi_add_device() to use a two
+ * stage registration sequence for each spi_device. This gives the caller
+ * some more control over the spi_device structure before it is registered,
+ * but requires that caller to initialize fields that would otherwise
+ * be defined using the board info.
  */
+extern struct spi_device *
+spi_alloc_device(struct spi_controller *ctlr);
+
+extern int
+spi_add_device(struct spi_device *spi);
+
 extern struct spi_device *
 spi_new_device(struct spi_controller *, struct spi_board_info *);
 
index 82d9daa..77426ff 100644 (file)
@@ -306,12 +306,19 @@ struct hda_codec {
 /*
  * constructors
  */
+__printf(3, 4) struct hda_codec *
+snd_hda_codec_device_init(struct hda_bus *bus, unsigned int codec_addr,
+                         const char *fmt, ...);
 int snd_hda_codec_new(struct hda_bus *bus, struct snd_card *card,
                      unsigned int codec_addr, struct hda_codec **codecp);
 int snd_hda_codec_device_new(struct hda_bus *bus, struct snd_card *card,
-                     unsigned int codec_addr, struct hda_codec *codec);
+                     unsigned int codec_addr, struct hda_codec *codec,
+                     bool snddev_managed);
 int snd_hda_codec_configure(struct hda_codec *codec);
 int snd_hda_codec_update_widgets(struct hda_codec *codec);
+void snd_hda_codec_register(struct hda_codec *codec);
+void snd_hda_codec_unregister(struct hda_codec *codec);
+void snd_hda_codec_cleanup_for_unbind(struct hda_codec *codec);
 
 /*
  * low level functions
@@ -490,9 +497,11 @@ int hda_call_check_power_status(struct hda_codec *codec, hda_nid_t nid)
 #define snd_hda_power_down(codec)      snd_hdac_power_down(&(codec)->core)
 #define snd_hda_power_down_pm(codec)   snd_hdac_power_down_pm(&(codec)->core)
 #ifdef CONFIG_PM
+void snd_hda_codec_set_power_save(struct hda_codec *codec, int delay);
 void snd_hda_set_power_save(struct hda_bus *bus, int delay);
 void snd_hda_update_power_acct(struct hda_codec *codec);
 #else
+static inline void snd_hda_codec_set_power_save(struct hda_codec *codec, int delay) {}
 static inline void snd_hda_set_power_save(struct hda_bus *bus, int delay) {}
 #endif
 
index e36b775..006d358 100644 (file)
@@ -461,7 +461,7 @@ enum {
 #define AC_DE_ELDV                     (1<<1)
 #define AC_DE_IA                       (1<<2)
 
-/* device device types (0x0-0xf) */
+/* device types (0x0-0xf) */
 enum {
        AC_JACK_LINE_OUT,
        AC_JACK_SPEAKER,
index 5a53879..d26234f 100644 (file)
@@ -30,6 +30,7 @@ void snd_hdac_ext_stream_spbcap_enable(struct hdac_bus *chip,
                                 bool enable, int index);
 
 int snd_hdac_ext_bus_get_ml_capabilities(struct hdac_bus *bus);
+struct hdac_ext_link *snd_hdac_ext_bus_link_at(struct hdac_bus *bus, int addr);
 struct hdac_ext_link *snd_hdac_ext_bus_get_link(struct hdac_bus *bus,
                                                const char *codec_name);
 
index 9b187d8..36da42c 100644 (file)
@@ -617,6 +617,7 @@ void snd_pcm_stream_unlock(struct snd_pcm_substream *substream);
 void snd_pcm_stream_lock_irq(struct snd_pcm_substream *substream);
 void snd_pcm_stream_unlock_irq(struct snd_pcm_substream *substream);
 unsigned long _snd_pcm_stream_lock_irqsave(struct snd_pcm_substream *substream);
+unsigned long _snd_pcm_stream_lock_irqsave_nested(struct snd_pcm_substream *substream);
 
 /**
  * snd_pcm_stream_lock_irqsave - Lock the PCM stream
@@ -635,6 +636,20 @@ unsigned long _snd_pcm_stream_lock_irqsave(struct snd_pcm_substream *substream);
 void snd_pcm_stream_unlock_irqrestore(struct snd_pcm_substream *substream,
                                      unsigned long flags);
 
+/**
+ * snd_pcm_stream_lock_irqsave_nested - Single-nested PCM stream locking
+ * @substream: PCM substream
+ * @flags: irq flags
+ *
+ * This locks the PCM stream like snd_pcm_stream_lock_irqsave() but with
+ * the single-depth lockdep subclass.
+ */
+#define snd_pcm_stream_lock_irqsave_nested(substream, flags)           \
+       do {                                                            \
+               typecheck(unsigned long, flags);                        \
+               flags = _snd_pcm_stream_lock_irqsave_nested(substream); \
+       } while (0)
+
 /**
  * snd_pcm_group_for_each_entry - iterate over the linked substreams
  * @s: the iterator
index ef0cafe..2d3e5df 100644 (file)
  *                                                                          *
  ****************************************************************************/
 
+#define AES_IEC958_STATUS_SIZE         24
+
 struct snd_aes_iec958 {
-       unsigned char status[24];       /* AES/IEC958 channel status bits */
+       unsigned char status[AES_IEC958_STATUS_SIZE]; /* AES/IEC958 channel status bits */
        unsigned char subcode[147];     /* AES/IEC958 subcode bits */
        unsigned char pad;              /* nothing */
        unsigned char dig_subframe[4];  /* AES/IEC958 subframe bits */
index db2e3c6..dd7b407 100644 (file)
@@ -84,7 +84,7 @@ config SND_PCM_TIMER
        help
          If you disable this option, pcm timer will be unavailable, so
          those stubs that use pcm timer (e.g. dmix, dsnoop & co) may work
-         incorrectlly.
+         incorrectly.
 
          For some embedded devices, we may disable it to reduce memory
          footprint, about 20KB on x86_64 platform.
index d1fcd1d..6fd763d 100644 (file)
@@ -511,7 +511,8 @@ static void *snd_dma_noncontig_alloc(struct snd_dma_buffer *dmab, size_t size)
                                      DEFAULT_GFP, 0);
        if (!sgt)
                return NULL;
-       dmab->dev.need_sync = dma_need_sync(dmab->dev.dev, dmab->dev.dir);
+       dmab->dev.need_sync = dma_need_sync(dmab->dev.dev,
+                                           sg_dma_address(sgt->sgl));
        p = dma_vmap_noncontiguous(dmab->dev.dev, size, sgt);
        if (p)
                dmab->private_data = sgt;
@@ -540,9 +541,9 @@ static void snd_dma_noncontig_sync(struct snd_dma_buffer *dmab,
        if (mode == SNDRV_DMA_SYNC_CPU) {
                if (dmab->dev.dir == DMA_TO_DEVICE)
                        return;
+               invalidate_kernel_vmap_range(dmab->area, dmab->bytes);
                dma_sync_sgtable_for_cpu(dmab->dev.dev, dmab->private_data,
                                         dmab->dev.dir);
-               invalidate_kernel_vmap_range(dmab->area, dmab->bytes);
        } else {
                if (dmab->dev.dir == DMA_FROM_DEVICE)
                        return;
@@ -671,9 +672,13 @@ static const struct snd_malloc_ops snd_dma_sg_wc_ops = {
  */
 static void *snd_dma_noncoherent_alloc(struct snd_dma_buffer *dmab, size_t size)
 {
-       dmab->dev.need_sync = dma_need_sync(dmab->dev.dev, dmab->dev.dir);
-       return dma_alloc_noncoherent(dmab->dev.dev, size, &dmab->addr,
-                                    dmab->dev.dir, DEFAULT_GFP);
+       void *p;
+
+       p = dma_alloc_noncoherent(dmab->dev.dev, size, &dmab->addr,
+                                 dmab->dev.dir, DEFAULT_GFP);
+       if (p)
+               dmab->dev.need_sync = dma_need_sync(dmab->dev.dev, dmab->addr);
+       return p;
 }
 
 static void snd_dma_noncoherent_free(struct snd_dma_buffer *dmab)
index 3ee9edf..90c3a36 100644 (file)
@@ -774,6 +774,11 @@ static int snd_pcm_oss_period_size(struct snd_pcm_substream *substream,
 
        if (oss_period_size < 16)
                return -EINVAL;
+
+       /* don't allocate too large period; 1MB period must be enough */
+       if (oss_period_size > 1024 * 1024)
+               return -ENOMEM;
+
        runtime->oss.period_bytes = oss_period_size;
        runtime->oss.period_frames = 1;
        runtime->oss.periods = oss_periods;
@@ -837,6 +842,17 @@ static void unlock_params(struct snd_pcm_runtime *runtime)
        mutex_unlock(&runtime->oss.params_lock);
 }
 
+static void snd_pcm_oss_release_buffers(struct snd_pcm_substream *substream)
+{
+       struct snd_pcm_runtime *runtime = substream->runtime;
+
+       kvfree(runtime->oss.buffer);
+       runtime->oss.buffer = NULL;
+#ifdef CONFIG_SND_PCM_OSS_PLUGINS
+       snd_pcm_oss_plugin_clear(substream);
+#endif
+}
+
 /* call with params_lock held */
 static int snd_pcm_oss_change_params_locked(struct snd_pcm_substream *substream)
 {
@@ -967,12 +983,10 @@ static int snd_pcm_oss_change_params_locked(struct snd_pcm_substream *substream)
        snd_pcm_oss_plugin_clear(substream);
        if (!direct) {
                /* add necessary plugins */
-               snd_pcm_oss_plugin_clear(substream);
                err = snd_pcm_plug_format_plugins(substream, params, sparams);
                if (err < 0) {
                        pcm_dbg(substream->pcm,
                                "snd_pcm_plug_format_plugins failed: %i\n", err);
-                       snd_pcm_oss_plugin_clear(substream);
                        goto failure;
                }
                if (runtime->oss.plugin_first) {
@@ -981,7 +995,6 @@ static int snd_pcm_oss_change_params_locked(struct snd_pcm_substream *substream)
                        if (err < 0) {
                                pcm_dbg(substream->pcm,
                                        "snd_pcm_plugin_build_io failed: %i\n", err);
-                               snd_pcm_oss_plugin_clear(substream);
                                goto failure;
                        }
                        if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
@@ -989,10 +1002,8 @@ static int snd_pcm_oss_change_params_locked(struct snd_pcm_substream *substream)
                        } else {
                                err = snd_pcm_plugin_insert(plugin);
                        }
-                       if (err < 0) {
-                               snd_pcm_oss_plugin_clear(substream);
+                       if (err < 0)
                                goto failure;
-                       }
                }
        }
 #endif
@@ -1043,10 +1054,9 @@ static int snd_pcm_oss_change_params_locked(struct snd_pcm_substream *substream)
                        goto failure;
        }
 #endif
-       oss_period_size *= oss_frame_size;
-
-       oss_buffer_size = oss_period_size * runtime->oss.periods;
-       if (oss_buffer_size < 0) {
+       oss_period_size = array_size(oss_period_size, oss_frame_size);
+       oss_buffer_size = array_size(oss_period_size, runtime->oss.periods);
+       if (oss_buffer_size <= 0) {
                err = -EINVAL;
                goto failure;
        }
@@ -1082,6 +1092,8 @@ static int snd_pcm_oss_change_params_locked(struct snd_pcm_substream *substream)
 
        err = 0;
 failure:
+       if (err)
+               snd_pcm_oss_release_buffers(substream);
        kfree(sw_params);
        kfree(params);
        kfree(sparams);
@@ -2351,13 +2363,7 @@ static void snd_pcm_oss_look_for_setup(struct snd_pcm *pcm, int stream,
 
 static void snd_pcm_oss_release_substream(struct snd_pcm_substream *substream)
 {
-       struct snd_pcm_runtime *runtime;
-       runtime = substream->runtime;
-       kvfree(runtime->oss.buffer);
-       runtime->oss.buffer = NULL;
-#ifdef CONFIG_SND_PCM_OSS_PLUGINS
-       snd_pcm_oss_plugin_clear(substream);
-#endif
+       snd_pcm_oss_release_buffers(substream);
        substream->oss.oss = 0;
 }
 
index 061ba06..82e180c 100644 (file)
@@ -62,7 +62,10 @@ static int snd_pcm_plugin_alloc(struct snd_pcm_plugin *plugin, snd_pcm_uframes_t
        width = snd_pcm_format_physical_width(format->format);
        if (width < 0)
                return width;
-       size = frames * format->channels * width;
+       size = array3_size(frames, format->channels, width);
+       /* check for too large period size once again */
+       if (size > 1024 * 1024)
+               return -ENOMEM;
        if (snd_BUG_ON(size % 8))
                return -ENXIO;
        size /= 8;
index 621883e..a056b3e 100644 (file)
@@ -172,6 +172,19 @@ unsigned long _snd_pcm_stream_lock_irqsave(struct snd_pcm_substream *substream)
 }
 EXPORT_SYMBOL_GPL(_snd_pcm_stream_lock_irqsave);
 
+unsigned long _snd_pcm_stream_lock_irqsave_nested(struct snd_pcm_substream *substream)
+{
+       unsigned long flags = 0;
+       if (substream->pcm->nonatomic)
+               mutex_lock_nested(&substream->self_group.mutex,
+                                 SINGLE_DEPTH_NESTING);
+       else
+               spin_lock_irqsave_nested(&substream->self_group.lock, flags,
+                                        SINGLE_DEPTH_NESTING);
+       return flags;
+}
+EXPORT_SYMBOL_GPL(_snd_pcm_stream_lock_irqsave_nested);
+
 /**
  * snd_pcm_stream_unlock_irqrestore - Unlock the PCM stream
  * @substream: PCM substream
index 0ee4a50..42d4e75 100644 (file)
@@ -66,7 +66,7 @@ snd_seq_oss_create_client(void)
        struct snd_seq_port_info *port;
        struct snd_seq_port_callback port_callback;
 
-       port = kmalloc(sizeof(*port), GFP_KERNEL);
+       port = kzalloc(sizeof(*port), GFP_KERNEL);
        if (!port) {
                rc = -ENOMEM;
                goto __error;
@@ -80,8 +80,7 @@ snd_seq_oss_create_client(void)
 
        system_client = rc;
 
-       /* create annoucement receiver port */
-       memset(port, 0, sizeof(*port));
+       /* create announcement receiver port */
        strcpy(port->name, "Receiver");
        port->addr.client = system_client;
        port->capability = SNDRV_SEQ_PORT_CAP_WRITE; /* receive only */
index bbfbebf..df44dd5 100644 (file)
@@ -240,9 +240,7 @@ int fcp_avc_transaction(struct fw_unit *unit,
        t.response_match_bytes = response_match_bytes;
        t.state = STATE_PENDING;
        init_waitqueue_head(&t.wait);
-
-       if (*(const u8 *)command == 0x00 || *(const u8 *)command == 0x03)
-               t.deferrable = true;
+       t.deferrable = (*(const u8 *)command == 0x00 || *(const u8 *)command == 0x03);
 
        spin_lock_irq(&transactions_lock);
        list_add_tail(&t.list, &transactions);
index b2df7b4..b072392 100644 (file)
@@ -132,6 +132,26 @@ void snd_hdac_link_free_all(struct hdac_bus *bus)
 }
 EXPORT_SYMBOL_GPL(snd_hdac_link_free_all);
 
+/**
+ * snd_hdac_ext_bus_link_at - get link at specified address
+ * @bus: link's parent bus device
+ * @addr: codec device address
+ *
+ * Returns link object or NULL if matching link is not found.
+ */
+struct hdac_ext_link *snd_hdac_ext_bus_link_at(struct hdac_bus *bus, int addr)
+{
+       struct hdac_ext_link *hlink;
+       int i;
+
+       list_for_each_entry(hlink, &bus->hlink_list, list)
+               for (i = 0; i < HDA_MAX_CODECS; i++)
+                       if (hlink->lsdiid & (0x1 << addr))
+                               return hlink;
+       return NULL;
+}
+EXPORT_SYMBOL_GPL(snd_hdac_ext_bus_link_at);
+
 /**
  * snd_hdac_ext_bus_get_link - get link based on codec name
  * @bus: the pointer to HDAC bus object
@@ -140,8 +160,6 @@ EXPORT_SYMBOL_GPL(snd_hdac_link_free_all);
 struct hdac_ext_link *snd_hdac_ext_bus_get_link(struct hdac_bus *bus,
                                                 const char *codec_name)
 {
-       int i;
-       struct hdac_ext_link *hlink = NULL;
        int bus_idx, addr;
 
        if (sscanf(codec_name, "ehdaudio%dD%d", &bus_idx, &addr) != 2)
@@ -151,14 +169,7 @@ struct hdac_ext_link *snd_hdac_ext_bus_get_link(struct hdac_bus *bus,
        if (addr < 0 || addr > 31)
                return NULL;
 
-       list_for_each_entry(hlink, &bus->hlink_list, list) {
-               for (i = 0; i < HDA_MAX_CODECS; i++) {
-                       if (hlink->lsdiid & (0x1 << addr))
-                               return hlink;
-               }
-       }
-
-       return NULL;
+       return snd_hdac_ext_bus_link_at(bus, addr);
 }
 EXPORT_SYMBOL_GPL(snd_hdac_ext_bus_get_link);
 
index 454474a..efe810a 100644 (file)
@@ -160,8 +160,8 @@ int snd_hdac_i915_init(struct hdac_bus *bus)
                if (!IS_ENABLED(CONFIG_MODULES) ||
                    !request_module("i915")) {
                        /* 60s timeout */
-                       wait_for_completion_timeout(&acomp->master_bind_complete,
-                                                   msecs_to_jiffies(60 * 1000));
+                       wait_for_completion_killable_timeout(&acomp->master_bind_complete,
+                                                            msecs_to_jiffies(60 * 1000));
                }
        }
        if (!acomp->ops) {
index b7758db..5cb92f7 100644 (file)
@@ -50,11 +50,11 @@ static bool is_link_enabled(struct fwnode_handle *fw_node, int i)
 static int
 sdw_intel_scan_controller(struct sdw_intel_acpi_info *info)
 {
-       struct acpi_device *adev;
+       struct acpi_device *adev = acpi_fetch_acpi_dev(info->handle);
        int ret, i;
        u8 count;
 
-       if (acpi_bus_get_device(info->handle, &adev))
+       if (!adev)
                return -EINVAL;
 
        /* Found controller, find links supported */
@@ -119,7 +119,6 @@ static acpi_status sdw_intel_acpi_cb(acpi_handle handle, u32 level,
                                     void *cdata, void **return_value)
 {
        struct sdw_intel_acpi_info *info = cdata;
-       struct acpi_device *adev;
        acpi_status status;
        u64 adr;
 
@@ -127,7 +126,7 @@ static acpi_status sdw_intel_acpi_cb(acpi_handle handle, u32 level,
        if (ACPI_FAILURE(status))
                return AE_OK; /* keep going */
 
-       if (acpi_bus_get_device(handle, &adev)) {
+       if (!acpi_fetch_acpi_dev(handle)) {
                pr_err("%s: Couldn't find ACPI handle\n", __func__);
                return AE_NOT_FOUND;
        }
index 463a6fe..bff6d85 100644 (file)
@@ -289,8 +289,7 @@ static int __init n64audio_probe(struct platform_device *pdev)
        struct snd_card *card;
        struct snd_pcm *pcm;
        struct n64audio *priv;
-       struct resource *res;
-       int err;
+       int err, irq;
 
        err = snd_card_new(&pdev->dev, SNDRV_DEFAULT_IDX1,
                           SNDRV_DEFAULT_STR1,
@@ -337,12 +336,12 @@ static int __init n64audio_probe(struct platform_device *pdev)
        strcpy(card->shortname, "N64 Audio");
        strcpy(card->longname, "N64 Audio");
 
-       res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
-       if (!res) {
+       irq = platform_get_irq(pdev, 0);
+       if (irq < 0) {
                err = -EINVAL;
                goto fail_dma_alloc;
        }
-       if (devm_request_irq(&pdev->dev, res->start, n64audio_isr,
+       if (devm_request_irq(&pdev->dev, irq, n64audio_isr,
                                IRQF_SHARED, "N64 Audio", priv)) {
                err = -EBUSY;
                goto fail_dma_alloc;
index f246e60..991b1c5 100644 (file)
 /* PCI function 0 registers, address = <val> + PCIBASE0                                                */
 /************************************************************************************************/
 
-#define PTR                    0x00            /* Indexed register set pointer register        */
+#define CA0106_PTR             0x00            /* Indexed register set pointer register        */
                                                /* NOTE: The CHANNELNUM and ADDRESS words can   */
                                                /* be modified independently of each other.     */
                                                /* CNL[1:0], ADDR[27:16]                        */
 
-#define DATA                   0x04            /* Indexed register set data register           */
+#define CA0106_DATA            0x04            /* Indexed register set data register           */
                                                /* DATA[31:0]                                   */
 
-#define IPR                    0x08            /* Global interrupt pending register            */
+#define CA0106_IPR             0x08            /* Global interrupt pending register            */
                                                /* Clear pending interrupts by writing a 1 to   */
                                                /* the relevant bits and zero to the other bits */
 #define IPR_MIDI_RX_B          0x00020000      /* MIDI UART-B Receive buffer non-empty         */
@@ -88,7 +88,7 @@
 #define IPR_MIDI_TX_A          0x00000002      /* MIDI UART-A Transmit buffer empty            */
 #define IPR_PCI                        0x00000001      /* PCI Bus error                                */
 
-#define INTE                   0x0c            /* Interrupt enable register                    */
+#define CA0106_INTE            0x0c            /* Interrupt enable register                    */
 
 #define INTE_MIDI_RX_B         0x00020000      /* MIDI UART-B Receive buffer non-empty         */
 #define INTE_MIDI_TX_B         0x00010000      /* MIDI UART-B Transmit buffer empty            */
 #define INTE_MIDI_TX_A         0x00000002      /* MIDI UART-A Transmit buffer empty            */
 #define INTE_PCI               0x00000001      /* PCI Bus error                                */
 
-#define UNKNOWN10              0x10            /* Unknown ??. Defaults to 0 */
-#define HCFG                   0x14            /* Hardware config register                     */
+#define CA0106_UNKNOWN10       0x10            /* Unknown ??. Defaults to 0 */
+#define CA0106_HCFG            0x14            /* Hardware config register                     */
                                                /* 0x1000 causes AC3 to fails. It adds a dither bit. */
 
 #define HCFG_STAC              0x10000000      /* Special mode for STAC9460 Codec. */
 #define HCFG_AUDIOENABLE       0x00000001      /* 0 = CODECs transmit zero-valued samples      */
                                                /* Should be set to 1 when the EMU10K1 is       */
                                                /* completely initialized.                      */
-#define GPIO                   0x18            /* Defaults: 005f03a3-Analog, 005f02a2-SPDIF.   */
+#define CA0106_GPIO            0x18            /* Defaults: 005f03a3-Analog, 005f02a2-SPDIF.   */
                                                /* Here pins 0,1,2,3,4,,6 are output. 5,7 are input */
                                                /* For the Audigy LS, pin 0 (or bit 8) controls the SPDIF/Analog jack. */
                                                /* SB Live 24bit:
                                                 * GPO [15:8] Default 0x9. (Default to SPDIF jack enabled for SPDIF)
                                                 * GPO Enable [23:16] Default 0x0f. Setting a bit to 1, causes the pin to be an output pin.
                                                 */
-#define AC97DATA               0x1c            /* AC97 register set data register (16 bit)     */
+#define CA0106_AC97DATA                0x1c            /* AC97 register set data register (16 bit)     */
 
-#define AC97ADDRESS            0x1e            /* AC97 register set address register (8 bit)   */
+#define CA0106_AC97ADDRESS     0x1e            /* AC97 register set address register (8 bit)   */
 
 /********************************************************************************************************/
 /* CA0106 pointer-offset register set, accessed through the PTR and DATA registers                     */
index 36fb150..8577f9f 100644 (file)
@@ -338,8 +338,8 @@ unsigned int snd_ca0106_ptr_read(struct snd_ca0106 * emu,
        regptr = (reg << 16) | chn;
 
        spin_lock_irqsave(&emu->emu_lock, flags);
-       outl(regptr, emu->port + PTR);
-       val = inl(emu->port + DATA);
+       outl(regptr, emu->port + CA0106_PTR);
+       val = inl(emu->port + CA0106_DATA);
        spin_unlock_irqrestore(&emu->emu_lock, flags);
        return val;
 }
@@ -355,8 +355,8 @@ void snd_ca0106_ptr_write(struct snd_ca0106 *emu,
        regptr = (reg << 16) | chn;
 
        spin_lock_irqsave(&emu->emu_lock, flags);
-       outl(regptr, emu->port + PTR);
-       outl(data, emu->port + DATA);
+       outl(regptr, emu->port + CA0106_PTR);
+       outl(data, emu->port + CA0106_DATA);
        spin_unlock_irqrestore(&emu->emu_lock, flags);
 }
 
@@ -455,8 +455,8 @@ static void snd_ca0106_intr_enable(struct snd_ca0106 *emu, unsigned int intrenb)
        unsigned int intr_enable;
 
        spin_lock_irqsave(&emu->emu_lock, flags);
-       intr_enable = inl(emu->port + INTE) | intrenb;
-       outl(intr_enable, emu->port + INTE);
+       intr_enable = inl(emu->port + CA0106_INTE) | intrenb;
+       outl(intr_enable, emu->port + CA0106_INTE);
        spin_unlock_irqrestore(&emu->emu_lock, flags);
 }
 
@@ -466,8 +466,8 @@ static void snd_ca0106_intr_disable(struct snd_ca0106 *emu, unsigned int intrenb
        unsigned int intr_enable;
 
        spin_lock_irqsave(&emu->emu_lock, flags);
-       intr_enable = inl(emu->port + INTE) & ~intrenb;
-       outl(intr_enable, emu->port + INTE);
+       intr_enable = inl(emu->port + CA0106_INTE) & ~intrenb;
+       outl(intr_enable, emu->port + CA0106_INTE);
        spin_unlock_irqrestore(&emu->emu_lock, flags);
 }
 
@@ -786,9 +786,9 @@ static int snd_ca0106_pcm_prepare_playback(struct snd_pcm_substream *substream)
                hcfg_set = 0;
                break;
        }
-       hcfg = inl(emu->port + HCFG) ;
+       hcfg = inl(emu->port + CA0106_HCFG) ;
        hcfg = (hcfg & ~hcfg_mask) | hcfg_set;
-       outl(hcfg, emu->port + HCFG);
+       outl(hcfg, emu->port + CA0106_HCFG);
        reg40 = snd_ca0106_ptr_read(emu, 0x40, 0);
        reg40 = (reg40 & ~reg40_mask) | reg40_set;
        snd_ca0106_ptr_write(emu, 0x40, 0, reg40);
@@ -888,9 +888,9 @@ static int snd_ca0106_pcm_prepare_capture(struct snd_pcm_substream *substream)
                hcfg_set = 0;
                break;
        }
-       hcfg = inl(emu->port + HCFG) ;
+       hcfg = inl(emu->port + CA0106_HCFG) ;
        hcfg = (hcfg & ~hcfg_mask) | hcfg_set;
-       outl(hcfg, emu->port + HCFG);
+       outl(hcfg, emu->port + CA0106_HCFG);
        reg71 = snd_ca0106_ptr_read(emu, 0x71, 0);
        reg71 = (reg71 & ~reg71_mask) | reg71_set;
        snd_ca0106_ptr_write(emu, 0x71, 0, reg71);
@@ -1142,8 +1142,8 @@ static unsigned short snd_ca0106_ac97_read(struct snd_ac97 *ac97,
        unsigned short val;
 
        spin_lock_irqsave(&emu->emu_lock, flags);
-       outb(reg, emu->port + AC97ADDRESS);
-       val = inw(emu->port + AC97DATA);
+       outb(reg, emu->port + CA0106_AC97ADDRESS);
+       val = inw(emu->port + CA0106_AC97DATA);
        spin_unlock_irqrestore(&emu->emu_lock, flags);
        return val;
 }
@@ -1155,8 +1155,8 @@ static void snd_ca0106_ac97_write(struct snd_ac97 *ac97,
        unsigned long flags;
   
        spin_lock_irqsave(&emu->emu_lock, flags);
-       outb(reg, emu->port + AC97ADDRESS);
-       outw(val, emu->port + AC97DATA);
+       outb(reg, emu->port + CA0106_AC97ADDRESS);
+       outw(val, emu->port + CA0106_AC97DATA);
        spin_unlock_irqrestore(&emu->emu_lock, flags);
 }
 
@@ -1200,7 +1200,7 @@ static irqreturn_t snd_ca0106_interrupt(int irq, void *dev_id)
         unsigned int stat76;
        struct snd_ca0106_channel *pchannel;
 
-       status = inl(chip->port + IPR);
+       status = inl(chip->port + CA0106_IPR);
        if (! status)
                return IRQ_NONE;
 
@@ -1255,7 +1255,7 @@ static irqreturn_t snd_ca0106_interrupt(int irq, void *dev_id)
        }
 
        // acknowledge the interrupt if necessary
-       outl(status, chip->port+IPR);
+       outl(status, chip->port + CA0106_IPR);
 
        return IRQ_HANDLED;
 }
@@ -1383,7 +1383,7 @@ static void ca0106_init_chip(struct snd_ca0106 *chip, int resume)
        int ch;
        unsigned int def_bits;
 
-       outl(0, chip->port + INTE);
+       outl(0, chip->port + CA0106_INTE);
 
        /*
         *  Init to 0x02109204 :
@@ -1420,8 +1420,8 @@ static void ca0106_init_chip(struct snd_ca0106 *chip, int resume)
         snd_ca0106_ptr_write(chip, CAPTURE_MUTE, 0, 0x00fc0000);
 
         /* Write 0x8000 to AC97_REC_GAIN to mute it. */
-        outb(AC97_REC_GAIN, chip->port + AC97ADDRESS);
-        outw(0x8000, chip->port + AC97DATA);
+        outb(AC97_REC_GAIN, chip->port + CA0106_AC97ADDRESS);
+        outw(0x8000, chip->port + CA0106_AC97DATA);
 #if 0 /* FIXME: what are these? */
        snd_ca0106_ptr_write(chip, SPCS0, 0, 0x2108006);
        snd_ca0106_ptr_write(chip, 0x42, 0, 0x2108006);
@@ -1495,30 +1495,30 @@ static void ca0106_init_chip(struct snd_ca0106 *chip, int resume)
                /* FIXME: Still need to find out what the other GPIO bits do.
                 * E.g. For digital spdif out.
                 */
-               outl(0x0, chip->port+GPIO);
-               /* outl(0x00f0e000, chip->port+GPIO); */ /* Analog */
-               outl(0x005f5301, chip->port+GPIO); /* Analog */
+               outl(0x0, chip->port + CA0106_GPIO);
+               /* outl(0x00f0e000, chip->port + CA0106_GPIO); */ /* Analog */
+               outl(0x005f5301, chip->port + CA0106_GPIO); /* Analog */
        } else if (chip->details->gpio_type == 1) {
                /* The SB0410 and SB0413 use GPIO differently. */
                /* FIXME: Still need to find out what the other GPIO bits do.
                 * E.g. For digital spdif out.
                 */
-               outl(0x0, chip->port+GPIO);
-               /* outl(0x00f0e000, chip->port+GPIO); */ /* Analog */
-               outl(0x005f5301, chip->port+GPIO); /* Analog */
+               outl(0x0, chip->port + CA0106_GPIO);
+               /* outl(0x00f0e000, chip->port + CA0106_GPIO); */ /* Analog */
+               outl(0x005f5301, chip->port + CA0106_GPIO); /* Analog */
        } else {
-               outl(0x0, chip->port+GPIO);
-               outl(0x005f03a3, chip->port+GPIO); /* Analog */
-               /* outl(0x005f02a2, chip->port+GPIO); */ /* SPDIF */
+               outl(0x0, chip->port + CA0106_GPIO);
+               outl(0x005f03a3, chip->port + CA0106_GPIO); /* Analog */
+               /* outl(0x005f02a2, chip->port + CA0106_GPIO); */ /* SPDIF */
        }
        snd_ca0106_intr_enable(chip, 0x105); /* Win2000 uses 0x1e0 */
 
        /* outl(HCFG_LOCKSOUNDCACHE|HCFG_AUDIOENABLE, chip->port+HCFG); */
        /* 0x1000 causes AC3 to fails. Maybe it effects 24 bit output. */
-       /* outl(0x00001409, chip->port+HCFG); */
-       /* outl(0x00000009, chip->port+HCFG); */
+       /* outl(0x00001409, chip->port + CA0106_HCFG); */
+       /* outl(0x00000009, chip->port + CA0106_HCFG); */
        /* AC97 2.0, Enable outputs. */
-       outl(HCFG_AC97 | HCFG_AUDIOENABLE, chip->port+HCFG);
+       outl(HCFG_AC97 | HCFG_AUDIOENABLE, chip->port + CA0106_HCFG);
 
        if (chip->details->i2c_adc == 1) {
                /* The SB0410 and SB0413 use I2C to control ADC. */
@@ -1560,12 +1560,12 @@ static void ca0106_stop_chip(struct snd_ca0106 *chip)
 {
        /* disable interrupts */
        snd_ca0106_ptr_write(chip, BASIC_INTERRUPT, 0, 0);
-       outl(0, chip->port + INTE);
+       outl(0, chip->port + CA0106_INTE);
        snd_ca0106_ptr_write(chip, EXTENDED_INT_MASK, 0, 0);
        udelay(1000);
        /* disable audio */
        /* outl(HCFG_LOCKSOUNDCACHE, chip->port + HCFG); */
-       outl(0, chip->port + HCFG);
+       outl(0, chip->port + CA0106_HCFG);
        /* FIXME: We need to stop and DMA transfers here.
         *        But as I am not sure how yet, we cannot from the dma pages.
         * So we can fix: snd-malloc: Memory leak?  pages not freed = 8
index c852c6a..05f5601 100644 (file)
@@ -70,8 +70,8 @@ static void ca0106_spdif_enable(struct snd_ca0106 *emu)
                snd_ca0106_ptr_write(emu, SPDIF_SELECT2, 0, 0x0b000000);
                val = snd_ca0106_ptr_read(emu, CAPTURE_CONTROL, 0) & ~0x1000;
                snd_ca0106_ptr_write(emu, CAPTURE_CONTROL, 0, val);
-               val = inl(emu->port + GPIO) & ~0x101;
-               outl(val, emu->port + GPIO);
+               val = inl(emu->port + CA0106_GPIO) & ~0x101;
+               outl(val, emu->port + CA0106_GPIO);
 
        } else {
                /* Analog */
@@ -79,8 +79,8 @@ static void ca0106_spdif_enable(struct snd_ca0106 *emu)
                snd_ca0106_ptr_write(emu, SPDIF_SELECT2, 0, 0x000f0000);
                val = snd_ca0106_ptr_read(emu, CAPTURE_CONTROL, 0) | 0x1000;
                snd_ca0106_ptr_write(emu, CAPTURE_CONTROL, 0, val);
-               val = inl(emu->port + GPIO) | 0x101;
-               outl(val, emu->port + GPIO);
+               val = inl(emu->port + CA0106_GPIO) | 0x101;
+               outl(val, emu->port + CA0106_GPIO);
        }
 }
 
@@ -119,14 +119,14 @@ static void ca0106_set_capture_mic_line_in(struct snd_ca0106 *emu)
 
        if (emu->capture_mic_line_in) {
                /* snd_ca0106_i2c_write(emu, ADC_MUX, 0); */ /* Mute input */
-               tmp = inl(emu->port+GPIO) & ~0x400;
+               tmp = inl(emu->port + CA0106_GPIO) & ~0x400;
                tmp = tmp | 0x400;
-               outl(tmp, emu->port+GPIO);
+               outl(tmp, emu->port + CA0106_GPIO);
                /* snd_ca0106_i2c_write(emu, ADC_MUX, ADC_MUX_MIC); */
        } else {
                /* snd_ca0106_i2c_write(emu, ADC_MUX, 0); */ /* Mute input */
-               tmp = inl(emu->port+GPIO) & ~0x400;
-               outl(tmp, emu->port+GPIO);
+               tmp = inl(emu->port + CA0106_GPIO) & ~0x400;
+               outl(tmp, emu->port + CA0106_GPIO);
                /* snd_ca0106_i2c_write(emu, ADC_MUX, ADC_MUX_LINEIN); */
        }
 }
index 9a678b5..dab801d 100644 (file)
@@ -298,7 +298,6 @@ MODULE_PARM_DESC(joystick_port, "Joystick port address.");
 #define CM_MICGAINZ            0x01    /* mic boost */
 #define CM_MICGAINZ_SHIFT      0
 
-#define CM_REG_MIXER3          0x24
 #define CM_REG_AUX_VOL         0x26
 #define CM_VAUXL_MASK          0xf0
 #define CM_VAUXR_MASK          0x0f
@@ -3265,7 +3264,7 @@ static int snd_cmipci_probe(struct pci_dev *pci,
  */
 static const unsigned char saved_regs[] = {
        CM_REG_FUNCTRL1, CM_REG_CHFORMAT, CM_REG_LEGACY_CTRL, CM_REG_MISC_CTRL,
-       CM_REG_MIXER0, CM_REG_MIXER1, CM_REG_MIXER2, CM_REG_MIXER3, CM_REG_PLL,
+       CM_REG_MIXER0, CM_REG_MIXER1, CM_REG_MIXER2, CM_REG_AUX_VOL, CM_REG_PLL,
        CM_REG_CH0_FRAME1, CM_REG_CH0_FRAME2,
        CM_REG_CH1_FRAME1, CM_REG_CH1_FRAME2, CM_REG_EXT_MISC,
        CM_REG_INT_STATUS, CM_REG_INT_HLDCLR, CM_REG_FUNCTRL0,
index cb72d27..7be5c33 100644 (file)
@@ -208,7 +208,7 @@ static void snd_echo_midi_output_write(struct timer_list *t)
 
        /* No interrupts are involved: we have to check at regular intervals
        if the card's output buffer has room for new data. */
-       sent = bytes = 0;
+       sent = 0;
        spin_lock_irqsave(&chip->lock, flags);
        chip->midi_full = 0;
        if (!snd_rawmidi_transmit_empty(chip->midi_out)) {
index febe1c2..9f6c99c 100644 (file)
@@ -285,15 +285,16 @@ config SND_HDA_INTEL_HDMI_SILENT_STREAM
        bool "Enable Silent Stream always for HDMI"
        depends on SND_HDA_INTEL
        help
-         Intel hardware has a feature called 'silent stream', that
-         keeps external HDMI receiver's analog circuitry powered on
-         avoiding 2-3 sec silence during playback start. This mechanism
-         relies on setting channel_id as 0xf, sending info packet and
-         preventing codec D3 entry (increasing  platform static power
-         consumption when HDMI receiver is plugged-in). 2-3 sec silence
-         at the playback start is expected whenever there is format change.
-         (default is 2 channel format).
-         Say Y to enable Silent Stream feature.
+         Say Y to enable HD-Audio Keep Alive (KAE) aka Silent Stream
+         for HDMI on hardware that supports the feature.
+
+         When enabled, the HDMI/DisplayPort codec will continue to provide
+         a continuous clock and a valid but silent data stream to
+         any connected external receiver. This allows to avoid gaps
+         at start of playback. Many receivers require multiple seconds
+         to start playing audio after the clock has been stopped.
+         This feature can impact power consumption as resources
+         are kept reserved both at transmitter and receiver.
 
 endif
 
index 82c492b..cd1db94 100644 (file)
@@ -981,7 +981,7 @@ void snd_hda_pick_fixup(struct hda_codec *codec,
        int id = HDA_FIXUP_ID_NOT_SET;
        const char *name = NULL;
        const char *type = NULL;
-       int vendor, device;
+       unsigned int vendor, device;
 
        if (codec->fixup_id != HDA_FIXUP_ID_NOT_SET)
                return;
index 7016b48..5cbac31 100644 (file)
@@ -766,6 +766,10 @@ static void codec_release_pcms(struct hda_codec *codec)
        }
 }
 
+/**
+ * snd_hda_codec_cleanup_for_unbind - Prepare codec for removal
+ * @codec: codec device to cleanup
+ */
 void snd_hda_codec_cleanup_for_unbind(struct hda_codec *codec)
 {
        if (codec->registered) {
@@ -813,7 +817,12 @@ void snd_hda_codec_display_power(struct hda_codec *codec, bool enable)
                snd_hdac_display_power(&codec->bus->core, codec->addr, enable);
 }
 
-/* also called from hda_bind.c */
+/**
+ * snd_hda_codec_register - Finalize codec initialization
+ * @codec: codec device to register
+ *
+ * Also called from hda_bind.c
+ */
 void snd_hda_codec_register(struct hda_codec *codec)
 {
        if (codec->registered)
@@ -826,6 +835,7 @@ void snd_hda_codec_register(struct hda_codec *codec)
                codec->registered = 1;
        }
 }
+EXPORT_SYMBOL_GPL(snd_hda_codec_register);
 
 static int snd_hda_codec_dev_register(struct snd_device *device)
 {
@@ -833,10 +843,12 @@ static int snd_hda_codec_dev_register(struct snd_device *device)
        return 0;
 }
 
-static int snd_hda_codec_dev_free(struct snd_device *device)
+/**
+ * snd_hda_codec_unregister - Unregister specified codec device
+ * @codec: codec device to unregister
+ */
+void snd_hda_codec_unregister(struct hda_codec *codec)
 {
-       struct hda_codec *codec = device->device_data;
-
        codec->in_freeing = 1;
        /*
         * snd_hda_codec_device_new() is used by legacy HDA and ASoC driver.
@@ -853,7 +865,12 @@ static int snd_hda_codec_dev_free(struct snd_device *device)
         */
        if (codec->core.type == HDA_DEV_LEGACY)
                put_device(hda_codec_dev(codec));
+}
+EXPORT_SYMBOL_GPL(snd_hda_codec_unregister);
 
+static int snd_hda_codec_dev_free(struct snd_device *device)
+{
+       snd_hda_codec_unregister(device->device_data);
        return 0;
 }
 
@@ -877,36 +894,48 @@ static void snd_hda_codec_dev_release(struct device *dev)
 
 #define DEV_NAME_LEN 31
 
-static int snd_hda_codec_device_init(struct hda_bus *bus, struct snd_card *card,
-                       unsigned int codec_addr, struct hda_codec **codecp)
+/**
+ * snd_hda_codec_device_init - allocate HDA codec device
+ * @bus: codec's parent bus
+ * @codec_addr: the codec address on the parent bus
+ * @fmt: format string for the device's name
+ *
+ * Returns newly allocated codec device or ERR_PTR() on failure.
+ */
+struct hda_codec *
+snd_hda_codec_device_init(struct hda_bus *bus, unsigned int codec_addr,
+                         const char *fmt, ...)
 {
+       va_list vargs;
        char name[DEV_NAME_LEN];
        struct hda_codec *codec;
        int err;
 
-       dev_dbg(card->dev, "%s: entry\n", __func__);
-
        if (snd_BUG_ON(!bus))
-               return -EINVAL;
+               return ERR_PTR(-EINVAL);
        if (snd_BUG_ON(codec_addr > HDA_MAX_CODEC_ADDRESS))
-               return -EINVAL;
+               return ERR_PTR(-EINVAL);
 
        codec = kzalloc(sizeof(*codec), GFP_KERNEL);
        if (!codec)
-               return -ENOMEM;
+               return ERR_PTR(-ENOMEM);
+
+       va_start(vargs, fmt);
+       vsprintf(name, fmt, vargs);
+       va_end(vargs);
 
-       sprintf(name, "hdaudioC%dD%d", card->number, codec_addr);
        err = snd_hdac_device_init(&codec->core, &bus->core, name, codec_addr);
        if (err < 0) {
                kfree(codec);
-               return err;
+               return ERR_PTR(err);
        }
 
+       codec->bus = bus;
        codec->core.type = HDA_DEV_LEGACY;
-       *codecp = codec;
 
-       return err;
+       return codec;
 }
+EXPORT_SYMBOL_GPL(snd_hda_codec_device_init);
 
 /**
  * snd_hda_codec_new - create a HDA codec
@@ -920,18 +949,21 @@ static int snd_hda_codec_device_init(struct hda_bus *bus, struct snd_card *card,
 int snd_hda_codec_new(struct hda_bus *bus, struct snd_card *card,
                      unsigned int codec_addr, struct hda_codec **codecp)
 {
-       int ret;
+       struct hda_codec *codec;
 
-       ret = snd_hda_codec_device_init(bus, card, codec_addr, codecp);
-       if (ret < 0)
-               return ret;
+       codec = snd_hda_codec_device_init(bus, codec_addr, "hdaudioC%dD%d",
+                                         card->number, codec_addr);
+       if (IS_ERR(codec))
+               return PTR_ERR(codec);
+       *codecp = codec;
 
-       return snd_hda_codec_device_new(bus, card, codec_addr, *codecp);
+       return snd_hda_codec_device_new(bus, card, codec_addr, *codecp, true);
 }
 EXPORT_SYMBOL_GPL(snd_hda_codec_new);
 
 int snd_hda_codec_device_new(struct hda_bus *bus, struct snd_card *card,
-                       unsigned int codec_addr, struct hda_codec *codec)
+                       unsigned int codec_addr, struct hda_codec *codec,
+                       bool snddev_managed)
 {
        char component[31];
        hda_nid_t fg;
@@ -951,7 +983,6 @@ int snd_hda_codec_device_new(struct hda_bus *bus, struct snd_card *card,
        codec->core.dev.release = snd_hda_codec_dev_release;
        codec->core.exec_verb = codec_exec_verb;
 
-       codec->bus = bus;
        codec->card = card;
        codec->addr = codec_addr;
        mutex_init(&codec->spdif_mutex);
@@ -1007,9 +1038,12 @@ int snd_hda_codec_device_new(struct hda_bus *bus, struct snd_card *card,
                codec->core.subsystem_id, codec->core.revision_id);
        snd_component_add(card, component);
 
-       err = snd_device_new(card, SNDRV_DEV_CODEC, codec, &dev_ops);
-       if (err < 0)
-               goto error;
+       if (snddev_managed) {
+               /* ASoC features component management instead */
+               err = snd_device_new(card, SNDRV_DEV_CODEC, codec, &dev_ops);
+               if (err < 0)
+                       goto error;
+       }
 
        /* PM runtime needs to be enabled later after binding codec */
        pm_runtime_forbid(&codec->core.dev);
@@ -3000,6 +3034,10 @@ void snd_hda_codec_shutdown(struct hda_codec *codec)
 {
        struct hda_pcm *cpcm;
 
+       /* Skip the shutdown if codec is not registered */
+       if (!codec->registered)
+               return;
+
        list_for_each_entry(cpcm, &codec->pcm_list_head, list)
                snd_pcm_suspend_all(cpcm->pcm);
 
@@ -3367,7 +3405,12 @@ int snd_hda_add_new_ctls(struct hda_codec *codec,
 EXPORT_SYMBOL_GPL(snd_hda_add_new_ctls);
 
 #ifdef CONFIG_PM
-static void codec_set_power_save(struct hda_codec *codec, int delay)
+/**
+ * snd_hda_codec_set_power_save - Configure codec's runtime PM
+ * @codec: codec device to configure
+ * @delay: autosuspend delay
+ */
+void snd_hda_codec_set_power_save(struct hda_codec *codec, int delay)
 {
        struct device *dev = hda_codec_dev(codec);
 
@@ -3385,6 +3428,7 @@ static void codec_set_power_save(struct hda_codec *codec, int delay)
                pm_runtime_forbid(dev);
        }
 }
+EXPORT_SYMBOL_GPL(snd_hda_codec_set_power_save);
 
 /**
  * snd_hda_set_power_save - reprogram autosuspend for the given delay
@@ -3398,7 +3442,7 @@ void snd_hda_set_power_save(struct hda_bus *bus, int delay)
        struct hda_codec *c;
 
        list_for_each_codec(c, bus)
-               codec_set_power_save(c, delay);
+               snd_hda_codec_set_power_save(c, delay);
 }
 EXPORT_SYMBOL_GPL(snd_hda_set_power_save);
 
index 3bf5e34..fc114e5 100644 (file)
@@ -91,6 +91,12 @@ static void snd_hda_gen_spec_free(struct hda_gen_spec *spec)
        free_kctls(spec);
        snd_array_free(&spec->paths);
        snd_array_free(&spec->loopback_list);
+#ifdef CONFIG_SND_HDA_GENERIC_LEDS
+       if (spec->led_cdevs[LED_AUDIO_MUTE])
+               led_classdev_unregister(spec->led_cdevs[LED_AUDIO_MUTE]);
+       if (spec->led_cdevs[LED_AUDIO_MICMUTE])
+               led_classdev_unregister(spec->led_cdevs[LED_AUDIO_MICMUTE]);
+#endif
 }
 
 /*
@@ -3922,7 +3928,10 @@ static int create_mute_led_cdev(struct hda_codec *codec,
                                                enum led_brightness),
                                bool micmute)
 {
+       struct hda_gen_spec *spec = codec->spec;
        struct led_classdev *cdev;
+       int idx = micmute ? LED_AUDIO_MICMUTE : LED_AUDIO_MUTE;
+       int err;
 
        cdev = devm_kzalloc(&codec->core.dev, sizeof(*cdev), GFP_KERNEL);
        if (!cdev)
@@ -3932,10 +3941,14 @@ static int create_mute_led_cdev(struct hda_codec *codec,
        cdev->max_brightness = 1;
        cdev->default_trigger = micmute ? "audio-micmute" : "audio-mute";
        cdev->brightness_set_blocking = callback;
-       cdev->brightness = ledtrig_audio_get(micmute ? LED_AUDIO_MICMUTE : LED_AUDIO_MUTE);
+       cdev->brightness = ledtrig_audio_get(idx);
        cdev->flags = LED_CORE_SUSPENDRESUME;
 
-       return devm_led_classdev_register(&codec->core.dev, cdev);
+       err = led_classdev_register(&codec->core.dev, cdev);
+       if (err < 0)
+               return err;
+       spec->led_cdevs[idx] = cdev;
+       return 0;
 }
 
 /**
index 8e1bc8e..34eba40 100644 (file)
@@ -294,6 +294,9 @@ struct hda_gen_spec {
                                   struct hda_jack_callback *cb);
        void (*mic_autoswitch_hook)(struct hda_codec *codec,
                                    struct hda_jack_callback *cb);
+
+       /* leds */
+       struct led_classdev *led_cdevs[NUM_AUDIO_LEDS];
 };
 
 /* values for add_stereo_mix_input flag */
index 4b0338c..0a83eb6 100644 (file)
@@ -1615,6 +1615,7 @@ static const struct snd_pci_quirk probe_mask_list[] = {
        /* forced codec slots */
        SND_PCI_QUIRK(0x1043, 0x1262, "ASUS W5Fm", 0x103),
        SND_PCI_QUIRK(0x1046, 0x1262, "ASUS W5F", 0x103),
+       SND_PCI_QUIRK(0x1558, 0x0351, "Schenker Dock 15", 0x105),
        /* WinFast VP200 H (Teradici) user reported broken communication */
        SND_PCI_QUIRK(0x3a21, 0x040d, "WinFast VP200 H", 0x101),
        {}
@@ -1798,8 +1799,6 @@ static int azx_create(struct snd_card *card, struct pci_dev *pci,
 
        assign_position_fix(chip, check_position_fix(chip, position_fix[dev]));
 
-       check_probe_mask(chip, dev);
-
        if (single_cmd < 0) /* allow fallback to single_cmd at errors */
                chip->fallback_to_single_cmd = 1;
        else /* explicitly set to single_cmd or not */
@@ -1825,6 +1824,8 @@ static int azx_create(struct snd_card *card, struct pci_dev *pci,
                chip->bus.core.needs_damn_long_delay = 1;
        }
 
+       check_probe_mask(chip, dev);
+
        err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, chip, &ops);
        if (err < 0) {
                dev_err(card->dev, "Error creating device [card]!\n");
@@ -1940,6 +1941,7 @@ static int azx_first_init(struct azx *chip)
                dma_bits = 32;
        if (dma_set_mask_and_coherent(&pci->dev, DMA_BIT_MASK(dma_bits)))
                dma_set_mask_and_coherent(&pci->dev, DMA_BIT_MASK(32));
+       dma_set_max_seg_size(&pci->dev, UINT_MAX);
 
        /* read number of streams from GCAP register instead of using
         * hardcoded value
@@ -2064,14 +2066,16 @@ static const struct hda_controller_ops pci_hda_ops = {
        .position_check = azx_position_check,
 };
 
+static DECLARE_BITMAP(probed_devs, SNDRV_CARDS);
+
 static int azx_probe(struct pci_dev *pci,
                     const struct pci_device_id *pci_id)
 {
-       static int dev;
        struct snd_card *card;
        struct hda_intel *hda;
        struct azx *chip;
        bool schedule_probe;
+       int dev;
        int err;
 
        if (pci_match_id(driver_denylist, pci)) {
@@ -2079,10 +2083,11 @@ static int azx_probe(struct pci_dev *pci,
                return -ENODEV;
        }
 
+       dev = find_first_zero_bit(probed_devs, SNDRV_CARDS);
        if (dev >= SNDRV_CARDS)
                return -ENODEV;
        if (!enable[dev]) {
-               dev++;
+               set_bit(dev, probed_devs);
                return -ENOENT;
        }
 
@@ -2149,7 +2154,7 @@ static int azx_probe(struct pci_dev *pci,
        if (schedule_probe)
                schedule_delayed_work(&hda->probe_work, 0);
 
-       dev++;
+       set_bit(dev, probed_devs);
        if (chip->disabled)
                complete_all(&hda->probe_wait);
        return 0;
@@ -2372,6 +2377,7 @@ static void azx_remove(struct pci_dev *pci)
                cancel_delayed_work_sync(&hda->probe_work);
                device_lock(&pci->dev);
 
+               clear_bit(chip->dev_index, probed_devs);
                pci_set_drvdata(pci, NULL);
                snd_card_free(card);
        }
@@ -2493,6 +2499,8 @@ static const struct pci_device_id azx_ids[] = {
        /* Alderlake-P */
        { PCI_DEVICE(0x8086, 0x51c8),
          .driver_data = AZX_DRIVER_SKL | AZX_DCAPS_INTEL_SKYLAKE},
+       { PCI_DEVICE(0x8086, 0x51c9),
+         .driver_data = AZX_DRIVER_SKL | AZX_DCAPS_INTEL_SKYLAKE},
        { PCI_DEVICE(0x8086, 0x51cd),
          .driver_data = AZX_DRIVER_SKL | AZX_DCAPS_INTEL_SKYLAKE},
        /* Alderlake-M */
@@ -2506,6 +2514,17 @@ static const struct pci_device_id azx_ids[] = {
          .driver_data = AZX_DRIVER_SKL | AZX_DCAPS_INTEL_SKYLAKE},
        { PCI_DEVICE(0x8086, 0x4b58),
          .driver_data = AZX_DRIVER_SKL | AZX_DCAPS_INTEL_SKYLAKE},
+       /* Raptor Lake */
+       { PCI_DEVICE(0x8086, 0x7a50),
+         .driver_data = AZX_DRIVER_SKL | AZX_DCAPS_INTEL_SKYLAKE},
+       { PCI_DEVICE(0x8086, 0x51ca),
+         .driver_data = AZX_DRIVER_SKL | AZX_DCAPS_INTEL_SKYLAKE},
+       { PCI_DEVICE(0x8086, 0x51cb),
+         .driver_data = AZX_DRIVER_SKL | AZX_DCAPS_INTEL_SKYLAKE},
+       { PCI_DEVICE(0x8086, 0x51ce),
+         .driver_data = AZX_DRIVER_SKL | AZX_DCAPS_INTEL_SKYLAKE},
+       { PCI_DEVICE(0x8086, 0x51cf),
+         .driver_data = AZX_DRIVER_SKL | AZX_DCAPS_INTEL_SKYLAKE},
        /* Broxton-P(Apollolake) */
        { PCI_DEVICE(0x8086, 0x5a98),
          .driver_data = AZX_DRIVER_SKL | AZX_DCAPS_INTEL_BROXTON },
index 8621f57..aca5926 100644 (file)
@@ -135,8 +135,6 @@ int __snd_hda_add_vmaster(struct hda_codec *codec, char *name,
 #define snd_hda_add_vmaster(codec, name, tlv, followers, suffix, access) \
        __snd_hda_add_vmaster(codec, name, tlv, followers, suffix, true, access, NULL)
 int snd_hda_codec_reset(struct hda_codec *codec);
-void snd_hda_codec_register(struct hda_codec *codec);
-void snd_hda_codec_cleanup_for_unbind(struct hda_codec *codec);
 void snd_hda_codec_disconnect_pcms(struct hda_codec *codec);
 
 #define snd_hda_regmap_sync(codec)     snd_hdac_regmap_sync(&(codec)->core)
index 773f490..2347d03 100644 (file)
@@ -70,6 +70,7 @@
 
 struct hda_tegra_soc {
        bool has_hda2codec_2x_reset;
+       bool has_hda2hdmi;
 };
 
 struct hda_tegra {
@@ -314,6 +315,18 @@ static int hda_tegra_first_init(struct azx *chip, struct platform_device *pdev)
         * hardcoded value
         */
        chip->capture_streams = (gcap >> 8) & 0x0f;
+
+       /* The GCAP register on Tegra234 implies no Input Streams(ISS) support,
+        * but the HW output stream descriptor programming should start with
+        * offset 0x20*4 from base stream descriptor address. This will be a
+        * problem while calculating the offset for output stream descriptor
+        * which will be considering input stream also. So here output stream
+        * starts with offset 0 which is wrong as HW register for output stream
+        * offset starts with 4.
+        */
+       if (of_device_is_compatible(np, "nvidia,tegra234-hda"))
+               chip->capture_streams = 4;
+
        chip->playback_streams = (gcap >> 12) & 0x0f;
        if (!chip->playback_streams && !chip->capture_streams) {
                /* gcap didn't give any info, switching to old method */
@@ -435,15 +448,23 @@ static int hda_tegra_create(struct snd_card *card,
 
 static const struct hda_tegra_soc tegra30_data = {
        .has_hda2codec_2x_reset = true,
+       .has_hda2hdmi = true,
 };
 
 static const struct hda_tegra_soc tegra194_data = {
        .has_hda2codec_2x_reset = false,
+       .has_hda2hdmi = true,
+};
+
+static const struct hda_tegra_soc tegra234_data = {
+       .has_hda2codec_2x_reset = true,
+       .has_hda2hdmi = false,
 };
 
 static const struct of_device_id hda_tegra_match[] = {
        { .compatible = "nvidia,tegra30-hda", .data = &tegra30_data },
        { .compatible = "nvidia,tegra194-hda", .data = &tegra194_data },
+       { .compatible = "nvidia,tegra234-hda", .data = &tegra234_data },
        {},
 };
 MODULE_DEVICE_TABLE(of, hda_tegra_match);
@@ -473,7 +494,14 @@ static int hda_tegra_probe(struct platform_device *pdev)
        }
 
        hda->resets[hda->nresets++].id = "hda";
-       hda->resets[hda->nresets++].id = "hda2hdmi";
+
+       /*
+        * "hda2hdmi" is not applicable for Tegra234. This is because the
+        * codec is separate IP and not under display SOR partition now.
+        */
+       if (hda->soc->has_hda2hdmi)
+               hda->resets[hda->nresets++].id = "hda2hdmi";
+
        /*
         * "hda2codec_2x" reset is not present on Tegra194. Though DT would
         * be updated to reflect this, but to have backward compatibility
@@ -488,7 +516,8 @@ static int hda_tegra_probe(struct platform_device *pdev)
                goto out_free;
 
        hda->clocks[hda->nclocks++].id = "hda";
-       hda->clocks[hda->nclocks++].id = "hda2hdmi";
+       if (hda->soc->has_hda2hdmi)
+               hda->clocks[hda->nclocks++].id = "hda2hdmi";
        hda->clocks[hda->nclocks++].id = "hda2codec_2x";
 
        err = devm_clk_bulk_get(&pdev->dev, hda->nclocks, hda->clocks);
index 92df4f2..c85ed7b 100644 (file)
@@ -120,6 +120,12 @@ struct hdmi_pcm {
        struct snd_kcontrol *eld_ctl;
 };
 
+enum {
+       SILENT_STREAM_OFF = 0,
+       SILENT_STREAM_KAE,      /* use standard HDA Keep-Alive */
+       SILENT_STREAM_I915,     /* Intel i915 extension */
+};
+
 struct hdmi_spec {
        struct hda_codec *codec;
        int num_cvts;
@@ -162,6 +168,8 @@ struct hdmi_spec {
        bool dyn_pin_out;
        bool dyn_pcm_assign;
        bool dyn_pcm_no_legacy;
+       /* hdmi interrupt trigger control flag for Nvidia codec */
+       bool hdmi_intr_trig_ctrl;
        bool intel_hsw_fixup;   /* apply Intel platform-specific fixups */
        /*
         * Non-generic VIA/NVIDIA specific
@@ -179,7 +187,7 @@ struct hdmi_spec {
        hda_nid_t vendor_nid;
        const int *port_map;
        int port_num;
-       bool send_silent_stream; /* Flag to enable silent stream feature */
+       int silent_stream_type;
 };
 
 #ifdef CONFIG_SND_HDA_COMPONENT
@@ -1665,18 +1673,71 @@ static void hdmi_present_sense_via_verbs(struct hdmi_spec_per_pin *per_pin,
 #define I915_SILENT_FORMAT_BITS        16
 #define I915_SILENT_FMT_MASK           0xf
 
+static void silent_stream_enable_i915(struct hda_codec *codec,
+                                     struct hdmi_spec_per_pin *per_pin)
+{
+       unsigned int format;
+
+       snd_hdac_sync_audio_rate(&codec->core, per_pin->pin_nid,
+                                per_pin->dev_id, I915_SILENT_RATE);
+
+       /* trigger silent stream generation in hw */
+       format = snd_hdac_calc_stream_format(I915_SILENT_RATE, I915_SILENT_CHANNELS,
+                                            I915_SILENT_FORMAT, I915_SILENT_FORMAT_BITS, 0);
+       snd_hda_codec_setup_stream(codec, per_pin->cvt_nid,
+                                  I915_SILENT_FMT_MASK, I915_SILENT_FMT_MASK, format);
+       usleep_range(100, 200);
+       snd_hda_codec_setup_stream(codec, per_pin->cvt_nid, I915_SILENT_FMT_MASK, 0, format);
+
+       per_pin->channels = I915_SILENT_CHANNELS;
+       hdmi_setup_audio_infoframe(codec, per_pin, per_pin->non_pcm);
+}
+
+static void silent_stream_set_kae(struct hda_codec *codec,
+                                 struct hdmi_spec_per_pin *per_pin,
+                                 bool enable)
+{
+       unsigned int param;
+
+       codec_dbg(codec, "HDMI: KAE %d cvt-NID=0x%x\n", enable, per_pin->cvt_nid);
+
+       param = snd_hda_codec_read(codec, per_pin->cvt_nid, 0, AC_VERB_GET_DIGI_CONVERT_1, 0);
+       param = (param >> 16) & 0xff;
+
+       if (enable)
+               param |= AC_DIG3_KAE;
+       else
+               param &= ~AC_DIG3_KAE;
+
+       snd_hda_codec_write(codec, per_pin->cvt_nid, 0, AC_VERB_SET_DIGI_CONVERT_3, param);
+}
+
 static void silent_stream_enable(struct hda_codec *codec,
                                 struct hdmi_spec_per_pin *per_pin)
 {
        struct hdmi_spec *spec = codec->spec;
        struct hdmi_spec_per_cvt *per_cvt;
        int cvt_idx, pin_idx, err;
-       unsigned int format;
+       int keep_power = 0;
+
+       /*
+        * Power-up will call hdmi_present_sense, so the PM calls
+        * have to be done without mutex held.
+        */
+
+       err = snd_hda_power_up_pm(codec);
+       if (err < 0 && err != -EACCES) {
+               codec_err(codec,
+                         "Failed to power up codec for silent stream enable ret=[%d]\n", err);
+               snd_hda_power_down_pm(codec);
+               return;
+       }
 
        mutex_lock(&per_pin->lock);
 
        if (per_pin->setup) {
                codec_dbg(codec, "hdmi: PCM already open, no silent stream\n");
+               err = -EBUSY;
                goto unlock_out;
        }
 
@@ -1703,22 +1764,23 @@ static void silent_stream_enable(struct hda_codec *codec,
        /* configure unused pins to choose other converters */
        pin_cvt_fixup(codec, per_pin, 0);
 
-       snd_hdac_sync_audio_rate(&codec->core, per_pin->pin_nid,
-                                per_pin->dev_id, I915_SILENT_RATE);
-
-       /* trigger silent stream generation in hw */
-       format = snd_hdac_calc_stream_format(I915_SILENT_RATE, I915_SILENT_CHANNELS,
-                                            I915_SILENT_FORMAT, I915_SILENT_FORMAT_BITS, 0);
-       snd_hda_codec_setup_stream(codec, per_pin->cvt_nid,
-                                  I915_SILENT_FMT_MASK, I915_SILENT_FMT_MASK, format);
-       usleep_range(100, 200);
-       snd_hda_codec_setup_stream(codec, per_pin->cvt_nid, I915_SILENT_FMT_MASK, 0, format);
-
-       per_pin->channels = I915_SILENT_CHANNELS;
-       hdmi_setup_audio_infoframe(codec, per_pin, per_pin->non_pcm);
+       switch (spec->silent_stream_type) {
+       case SILENT_STREAM_KAE:
+               silent_stream_set_kae(codec, per_pin, true);
+               break;
+       case SILENT_STREAM_I915:
+               silent_stream_enable_i915(codec, per_pin);
+               keep_power = 1;
+               break;
+       default:
+               break;
+       }
 
  unlock_out:
        mutex_unlock(&per_pin->lock);
+
+       if (err || !keep_power)
+               snd_hda_power_down_pm(codec);
 }
 
 static void silent_stream_disable(struct hda_codec *codec,
@@ -1726,7 +1788,16 @@ static void silent_stream_disable(struct hda_codec *codec,
 {
        struct hdmi_spec *spec = codec->spec;
        struct hdmi_spec_per_cvt *per_cvt;
-       int cvt_idx;
+       int cvt_idx, err;
+
+       err = snd_hda_power_up_pm(codec);
+       if (err < 0 && err != -EACCES) {
+               codec_err(codec,
+                         "Failed to power up codec for silent stream disable ret=[%d]\n",
+                         err);
+               snd_hda_power_down_pm(codec);
+               return;
+       }
 
        mutex_lock(&per_pin->lock);
        if (!per_pin->silent_stream)
@@ -1741,11 +1812,20 @@ static void silent_stream_disable(struct hda_codec *codec,
                per_cvt->assigned = 0;
        }
 
+       if (spec->silent_stream_type == SILENT_STREAM_I915) {
+               /* release ref taken in silent_stream_enable() */
+               snd_hda_power_down_pm(codec);
+       } else if (spec->silent_stream_type == SILENT_STREAM_KAE) {
+               silent_stream_set_kae(codec, per_pin, false);
+       }
+
        per_pin->cvt_nid = 0;
        per_pin->silent_stream = false;
 
  unlock_out:
        mutex_unlock(&per_pin->lock);
+
+       snd_hda_power_down_pm(codec);
 }
 
 /* update ELD and jack state via audio component */
@@ -1767,29 +1847,11 @@ static void sync_eld_via_acomp(struct hda_codec *codec,
        monitor_next = per_pin->sink_eld.monitor_present;
        mutex_unlock(&per_pin->lock);
 
-       /*
-        * Power-up will call hdmi_present_sense, so the PM calls
-        * have to be done without mutex held.
-        */
-
-       if (spec->send_silent_stream) {
-               int pm_ret;
-
-               if (!monitor_prev && monitor_next) {
-                       pm_ret = snd_hda_power_up_pm(codec);
-                       if (pm_ret < 0)
-                               codec_err(codec,
-                               "Monitor plugged-in, Failed to power up codec ret=[%d]\n",
-                               pm_ret);
+       if (spec->silent_stream_type) {
+               if (!monitor_prev && monitor_next)
                        silent_stream_enable(codec, per_pin);
-               } else if (monitor_prev && !monitor_next) {
+               else if (monitor_prev && !monitor_next)
                        silent_stream_disable(codec, per_pin);
-                       pm_ret = snd_hda_power_down_pm(codec);
-                       if (pm_ret < 0)
-                               codec_err(codec,
-                               "Monitor plugged-out, Failed to power down codec ret=[%d]\n",
-                               pm_ret);
-               }
        }
 }
 
@@ -2982,7 +3044,7 @@ static int intel_hsw_common_init(struct hda_codec *codec, hda_nid_t vendor_nid,
         * module param or Kconfig option
         */
        if (send_silent_stream)
-               spec->send_silent_stream = true;
+               spec->silent_stream_type = SILENT_STREAM_I915;
 
        return parse_intel_hdmi(codec);
 }
@@ -3035,6 +3097,22 @@ static int patch_i915_tgl_hdmi(struct hda_codec *codec)
        return ret;
 }
 
+static int patch_i915_adlp_hdmi(struct hda_codec *codec)
+{
+       struct hdmi_spec *spec;
+       int res;
+
+       res = patch_i915_tgl_hdmi(codec);
+       if (!res) {
+               spec = codec->spec;
+
+               if (spec->silent_stream_type)
+                       spec->silent_stream_type = SILENT_STREAM_KAE;
+       }
+
+       return res;
+}
+
 /* Intel Baytrail and Braswell; with eld notifier */
 static int patch_i915_byt_hdmi(struct hda_codec *codec)
 {
@@ -3721,8 +3799,11 @@ static int patch_nvhdmi_legacy(struct hda_codec *codec)
  * +-----------------------------------|
  *
  * Note that for the trigger bit to take effect it needs to change value
- * (i.e. it needs to be toggled).
+ * (i.e. it needs to be toggled). The trigger bit is not applicable from
+ * TEGRA234 chip onwards, as new verb id 0xf80 will be used for interrupt
+ * trigger to hdmi.
  */
+#define NVIDIA_SET_HOST_INTR           0xf80
 #define NVIDIA_GET_SCRATCH0            0xfa6
 #define NVIDIA_SET_SCRATCH0_BYTE0      0xfa7
 #define NVIDIA_SET_SCRATCH0_BYTE1      0xfa8
@@ -3741,25 +3822,38 @@ static int patch_nvhdmi_legacy(struct hda_codec *codec)
  * The format parameter is the HDA audio format (see AC_FMT_*). If set to 0,
  * the format is invalidated so that the HDMI codec can be disabled.
  */
-static void tegra_hdmi_set_format(struct hda_codec *codec, unsigned int format)
+static void tegra_hdmi_set_format(struct hda_codec *codec,
+                                 hda_nid_t cvt_nid,
+                                 unsigned int format)
 {
        unsigned int value;
+       unsigned int nid = NVIDIA_AFG_NID;
+       struct hdmi_spec *spec = codec->spec;
+
+       /*
+        * Tegra HDA codec design from TEGRA234 chip onwards support DP MST.
+        * This resulted in moving scratch registers from audio function
+        * group to converter widget context. So CVT NID should be used for
+        * scratch register read/write for DP MST supported Tegra HDA codec.
+        */
+       if (codec->dp_mst)
+               nid = cvt_nid;
 
        /* bits [31:30] contain the trigger and valid bits */
-       value = snd_hda_codec_read(codec, NVIDIA_AFG_NID, 0,
+       value = snd_hda_codec_read(codec, nid, 0,
                                   NVIDIA_GET_SCRATCH0, 0);
        value = (value >> 24) & 0xff;
 
        /* bits [15:0] are used to store the HDA format */
-       snd_hda_codec_write(codec, NVIDIA_AFG_NID, 0,
+       snd_hda_codec_write(codec, nid, 0,
                            NVIDIA_SET_SCRATCH0_BYTE0,
                            (format >> 0) & 0xff);
-       snd_hda_codec_write(codec, NVIDIA_AFG_NID, 0,
+       snd_hda_codec_write(codec, nid, 0,
                            NVIDIA_SET_SCRATCH0_BYTE1,
                            (format >> 8) & 0xff);
 
        /* bits [16:24] are unused */
-       snd_hda_codec_write(codec, NVIDIA_AFG_NID, 0,
+       snd_hda_codec_write(codec, nid, 0,
                            NVIDIA_SET_SCRATCH0_BYTE2, 0);
 
        /*
@@ -3771,15 +3865,28 @@ static void tegra_hdmi_set_format(struct hda_codec *codec, unsigned int format)
        else
                value |= NVIDIA_SCRATCH_VALID;
 
-       /*
-        * Whenever the trigger bit is toggled, an interrupt is raised in the
-        * HDMI codec. The HDMI driver will use that as trigger to update its
-        * configuration.
-        */
-       value ^= NVIDIA_SCRATCH_TRIGGER;
+       if (spec->hdmi_intr_trig_ctrl) {
+               /*
+                * For Tegra HDA Codec design from TEGRA234 onwards, the
+                * Interrupt to hdmi driver is triggered by writing
+                * non-zero values to verb 0xF80 instead of 31st bit of
+                * scratch register.
+                */
+               snd_hda_codec_write(codec, nid, 0,
+                               NVIDIA_SET_SCRATCH0_BYTE3, value);
+               snd_hda_codec_write(codec, nid, 0,
+                               NVIDIA_SET_HOST_INTR, 0x1);
+       } else {
+               /*
+                * Whenever the 31st trigger bit is toggled, an interrupt is raised
+                * in the HDMI codec. The HDMI driver will use that as trigger
+                * to update its configuration.
+                */
+               value ^= NVIDIA_SCRATCH_TRIGGER;
 
-       snd_hda_codec_write(codec, NVIDIA_AFG_NID, 0,
-                           NVIDIA_SET_SCRATCH0_BYTE3, value);
+               snd_hda_codec_write(codec, nid, 0,
+                               NVIDIA_SET_SCRATCH0_BYTE3, value);
+       }
 }
 
 static int tegra_hdmi_pcm_prepare(struct hda_pcm_stream *hinfo,
@@ -3796,7 +3903,7 @@ static int tegra_hdmi_pcm_prepare(struct hda_pcm_stream *hinfo,
                return err;
 
        /* notify the HDMI codec of the format change */
-       tegra_hdmi_set_format(codec, format);
+       tegra_hdmi_set_format(codec, hinfo->nid, format);
 
        return 0;
 }
@@ -3806,7 +3913,7 @@ static int tegra_hdmi_pcm_cleanup(struct hda_pcm_stream *hinfo,
                                  struct snd_pcm_substream *substream)
 {
        /* invalidate the format in the HDMI codec */
-       tegra_hdmi_set_format(codec, 0);
+       tegra_hdmi_set_format(codec, hinfo->nid, 0);
 
        return generic_hdmi_playback_pcm_cleanup(hinfo, codec, substream);
 }
@@ -3851,17 +3958,29 @@ static int tegra_hdmi_build_pcms(struct hda_codec *codec)
        return 0;
 }
 
-static int patch_tegra_hdmi(struct hda_codec *codec)
+static int tegra_hdmi_init(struct hda_codec *codec)
 {
-       struct hdmi_spec *spec;
-       int err;
+       struct hdmi_spec *spec = codec->spec;
+       int i, err;
 
-       err = patch_generic_hdmi(codec);
-       if (err)
+       err = hdmi_parse_codec(codec);
+       if (err < 0) {
+               generic_spec_free(codec);
                return err;
+       }
+
+       for (i = 0; i < spec->num_cvts; i++)
+               snd_hda_codec_write(codec, spec->cvt_nids[i], 0,
+                                       AC_VERB_SET_DIGI_CONVERT_1,
+                                       AC_DIG1_ENABLE);
+
+       generic_hdmi_init_per_pins(codec);
 
        codec->patch_ops.build_pcms = tegra_hdmi_build_pcms;
-       spec = codec->spec;
+       spec->chmap.ops.chmap_cea_alloc_validate_get_type =
+               nvhdmi_chmap_cea_alloc_validate_get_type;
+       spec->chmap.ops.chmap_validate = nvhdmi_chmap_validate;
+
        spec->chmap.ops.chmap_cea_alloc_validate_get_type =
                nvhdmi_chmap_cea_alloc_validate_get_type;
        spec->chmap.ops.chmap_validate = nvhdmi_chmap_validate;
@@ -3869,6 +3988,36 @@ static int patch_tegra_hdmi(struct hda_codec *codec)
        return 0;
 }
 
+static int patch_tegra_hdmi(struct hda_codec *codec)
+{
+       int err;
+
+       err = alloc_generic_hdmi(codec);
+       if (err < 0)
+               return err;
+
+       return tegra_hdmi_init(codec);
+}
+
+static int patch_tegra234_hdmi(struct hda_codec *codec)
+{
+       struct hdmi_spec *spec;
+       int err;
+
+       err = alloc_generic_hdmi(codec);
+       if (err < 0)
+               return err;
+
+       codec->dp_mst = true;
+       codec->mst_no_extra_pcms = true;
+       spec = codec->spec;
+       spec->dyn_pin_out = true;
+       spec->dyn_pcm_assign = true;
+       spec->hdmi_intr_trig_ctrl = true;
+
+       return tegra_hdmi_init(codec);
+}
+
 /*
  * ATI/AMD-specific implementations
  */
@@ -4322,6 +4471,7 @@ HDA_CODEC_ENTRY(0x10de002d, "Tegra186 HDMI/DP0", patch_tegra_hdmi),
 HDA_CODEC_ENTRY(0x10de002e, "Tegra186 HDMI/DP1", patch_tegra_hdmi),
 HDA_CODEC_ENTRY(0x10de002f, "Tegra194 HDMI/DP2", patch_tegra_hdmi),
 HDA_CODEC_ENTRY(0x10de0030, "Tegra194 HDMI/DP3", patch_tegra_hdmi),
+HDA_CODEC_ENTRY(0x10de0031, "Tegra234 HDMI/DP", patch_tegra234_hdmi),
 HDA_CODEC_ENTRY(0x10de0040, "GPU 40 HDMI/DP",  patch_nvhdmi),
 HDA_CODEC_ENTRY(0x10de0041, "GPU 41 HDMI/DP",  patch_nvhdmi),
 HDA_CODEC_ENTRY(0x10de0042, "GPU 42 HDMI/DP",  patch_nvhdmi),
@@ -4390,10 +4540,11 @@ HDA_CODEC_ENTRY(0x80862812, "Tigerlake HDMI",   patch_i915_tgl_hdmi),
 HDA_CODEC_ENTRY(0x80862814, "DG1 HDMI",        patch_i915_tgl_hdmi),
 HDA_CODEC_ENTRY(0x80862815, "Alderlake HDMI",  patch_i915_tgl_hdmi),
 HDA_CODEC_ENTRY(0x80862816, "Rocketlake HDMI", patch_i915_tgl_hdmi),
-HDA_CODEC_ENTRY(0x80862819, "DG2 HDMI",        patch_i915_tgl_hdmi),
+HDA_CODEC_ENTRY(0x80862818, "Raptorlake HDMI", patch_i915_tgl_hdmi),
+HDA_CODEC_ENTRY(0x80862819, "DG2 HDMI",        patch_i915_adlp_hdmi),
 HDA_CODEC_ENTRY(0x8086281a, "Jasperlake HDMI", patch_i915_icl_hdmi),
 HDA_CODEC_ENTRY(0x8086281b, "Elkhartlake HDMI",        patch_i915_icl_hdmi),
-HDA_CODEC_ENTRY(0x8086281c, "Alderlake-P HDMI", patch_i915_tgl_hdmi),
+HDA_CODEC_ENTRY(0x8086281c, "Alderlake-P HDMI", patch_i915_adlp_hdmi),
 HDA_CODEC_ENTRY(0x80862880, "CedarTrail HDMI", patch_generic_hdmi),
 HDA_CODEC_ENTRY(0x80862882, "Valleyview2 HDMI",        patch_i915_byt_hdmi),
 HDA_CODEC_ENTRY(0x80862883, "Braswell HDMI",   patch_i915_byt_hdmi),
index 668274e..1d14be4 100644 (file)
@@ -98,6 +98,7 @@ struct alc_spec {
        unsigned int gpio_mic_led_mask;
        struct alc_coef_led mute_led_coef;
        struct alc_coef_led mic_led_coef;
+       struct mutex coef_mutex;
 
        hda_nid_t headset_mic_pin;
        hda_nid_t headphone_mic_pin;
@@ -137,8 +138,24 @@ struct alc_spec {
  * COEF access helper functions
  */
 
-static int alc_read_coefex_idx(struct hda_codec *codec, hda_nid_t nid,
-                              unsigned int coef_idx)
+static void coef_mutex_lock(struct hda_codec *codec)
+{
+       struct alc_spec *spec = codec->spec;
+
+       snd_hda_power_up_pm(codec);
+       mutex_lock(&spec->coef_mutex);
+}
+
+static void coef_mutex_unlock(struct hda_codec *codec)
+{
+       struct alc_spec *spec = codec->spec;
+
+       mutex_unlock(&spec->coef_mutex);
+       snd_hda_power_down_pm(codec);
+}
+
+static int __alc_read_coefex_idx(struct hda_codec *codec, hda_nid_t nid,
+                                unsigned int coef_idx)
 {
        unsigned int val;
 
@@ -147,28 +164,56 @@ static int alc_read_coefex_idx(struct hda_codec *codec, hda_nid_t nid,
        return val;
 }
 
+static int alc_read_coefex_idx(struct hda_codec *codec, hda_nid_t nid,
+                              unsigned int coef_idx)
+{
+       unsigned int val;
+
+       coef_mutex_lock(codec);
+       val = __alc_read_coefex_idx(codec, nid, coef_idx);
+       coef_mutex_unlock(codec);
+       return val;
+}
+
 #define alc_read_coef_idx(codec, coef_idx) \
        alc_read_coefex_idx(codec, 0x20, coef_idx)
 
-static void alc_write_coefex_idx(struct hda_codec *codec, hda_nid_t nid,
-                                unsigned int coef_idx, unsigned int coef_val)
+static void __alc_write_coefex_idx(struct hda_codec *codec, hda_nid_t nid,
+                                  unsigned int coef_idx, unsigned int coef_val)
 {
        snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_COEF_INDEX, coef_idx);
        snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_PROC_COEF, coef_val);
 }
 
+static void alc_write_coefex_idx(struct hda_codec *codec, hda_nid_t nid,
+                                unsigned int coef_idx, unsigned int coef_val)
+{
+       coef_mutex_lock(codec);
+       __alc_write_coefex_idx(codec, nid, coef_idx, coef_val);
+       coef_mutex_unlock(codec);
+}
+
 #define alc_write_coef_idx(codec, coef_idx, coef_val) \
        alc_write_coefex_idx(codec, 0x20, coef_idx, coef_val)
 
+static void __alc_update_coefex_idx(struct hda_codec *codec, hda_nid_t nid,
+                                   unsigned int coef_idx, unsigned int mask,
+                                   unsigned int bits_set)
+{
+       unsigned int val = __alc_read_coefex_idx(codec, nid, coef_idx);
+
+       if (val != -1)
+               __alc_write_coefex_idx(codec, nid, coef_idx,
+                                      (val & ~mask) | bits_set);
+}
+
 static void alc_update_coefex_idx(struct hda_codec *codec, hda_nid_t nid,
                                  unsigned int coef_idx, unsigned int mask,
                                  unsigned int bits_set)
 {
-       unsigned int val = alc_read_coefex_idx(codec, nid, coef_idx);
-
-       if (val != -1)
-               alc_write_coefex_idx(codec, nid, coef_idx,
-                                    (val & ~mask) | bits_set);
+       coef_mutex_lock(codec);
+       __alc_update_coefex_idx(codec, nid, coef_idx, mask, bits_set);
+       coef_mutex_unlock(codec);
 }
 
 #define alc_update_coef_idx(codec, coef_idx, mask, bits_set)   \
@@ -201,13 +246,15 @@ struct coef_fw {
 static void alc_process_coef_fw(struct hda_codec *codec,
                                const struct coef_fw *fw)
 {
+       coef_mutex_lock(codec);
        for (; fw->nid; fw++) {
                if (fw->mask == (unsigned short)-1)
-                       alc_write_coefex_idx(codec, fw->nid, fw->idx, fw->val);
+                       __alc_write_coefex_idx(codec, fw->nid, fw->idx, fw->val);
                else
-                       alc_update_coefex_idx(codec, fw->nid, fw->idx,
-                                             fw->mask, fw->val);
+                       __alc_update_coefex_idx(codec, fw->nid, fw->idx,
+                                               fw->mask, fw->val);
        }
+       coef_mutex_unlock(codec);
 }
 
 /*
@@ -1153,6 +1200,7 @@ static int alc_alloc_spec(struct hda_codec *codec, hda_nid_t mixer_nid)
        codec->spdif_status_reset = 1;
        codec->forced_resume = 1;
        codec->patch_ops = alc_patch_ops;
+       mutex_init(&spec->coef_mutex);
 
        err = alc_codec_rename_from_preset(codec);
        if (err < 0) {
@@ -2125,6 +2173,7 @@ static void alc1220_fixup_gb_x570(struct hda_codec *codec,
 {
        static const hda_nid_t conn1[] = { 0x0c };
        static const struct coef_fw gb_x570_coefs[] = {
+               WRITE_COEF(0x07, 0x03c0),
                WRITE_COEF(0x1a, 0x01c1),
                WRITE_COEF(0x1b, 0x0202),
                WRITE_COEF(0x43, 0x3005),
@@ -2551,7 +2600,8 @@ static const struct snd_pci_quirk alc882_fixup_tbl[] = {
        SND_PCI_QUIRK(0x1458, 0xa002, "Gigabyte EP45-DS3/Z87X-UD3H", ALC889_FIXUP_FRONT_HP_NO_PRESENCE),
        SND_PCI_QUIRK(0x1458, 0xa0b8, "Gigabyte AZ370-Gaming", ALC1220_FIXUP_GB_DUAL_CODECS),
        SND_PCI_QUIRK(0x1458, 0xa0cd, "Gigabyte X570 Aorus Master", ALC1220_FIXUP_GB_X570),
-       SND_PCI_QUIRK(0x1458, 0xa0ce, "Gigabyte X570 Aorus Xtreme", ALC1220_FIXUP_CLEVO_P950),
+       SND_PCI_QUIRK(0x1458, 0xa0ce, "Gigabyte X570 Aorus Xtreme", ALC1220_FIXUP_GB_X570),
+       SND_PCI_QUIRK(0x1458, 0xa0d5, "Gigabyte X570S Aorus Master", ALC1220_FIXUP_GB_X570),
        SND_PCI_QUIRK(0x1462, 0x11f7, "MSI-GE63", ALC1220_FIXUP_CLEVO_P950),
        SND_PCI_QUIRK(0x1462, 0x1228, "MSI-GP63", ALC1220_FIXUP_CLEVO_P950),
        SND_PCI_QUIRK(0x1462, 0x1229, "MSI-GP73", ALC1220_FIXUP_CLEVO_P950),
@@ -2626,6 +2676,7 @@ static const struct hda_model_fixup alc882_fixup_models[] = {
        {.id = ALC882_FIXUP_NO_PRIMARY_HP, .name = "no-primary-hp"},
        {.id = ALC887_FIXUP_ASUS_BASS, .name = "asus-bass"},
        {.id = ALC1220_FIXUP_GB_DUAL_CODECS, .name = "dual-codecs"},
+       {.id = ALC1220_FIXUP_GB_X570, .name = "gb-x570"},
        {.id = ALC1220_FIXUP_CLEVO_P950, .name = "clevo-p950"},
        {}
 };
@@ -6611,6 +6662,16 @@ static void cs35l41_fixup_i2c_two(struct hda_codec *cdc, const struct hda_fixup
        cs35l41_generic_fixup(cdc, action, "i2c", "CSC3551", 2);
 }
 
+static void cs35l41_fixup_spi_two(struct hda_codec *codec, const struct hda_fixup *fix, int action)
+{
+       cs35l41_generic_fixup(codec, action, "spi0", "CSC3551", 2);
+}
+
+static void cs35l41_fixup_spi_four(struct hda_codec *codec, const struct hda_fixup *fix, int action)
+{
+       cs35l41_generic_fixup(codec, action, "spi0", "CSC3551", 4);
+}
+
 static void alc287_legion_16achg6_playback_hook(struct hda_pcm_stream *hinfo, struct hda_codec *cdc,
                                                struct snd_pcm_substream *sub, int action)
 {
@@ -6948,6 +7009,9 @@ enum {
        ALC285_FIXUP_LEGION_Y9000X_AUTOMUTE,
        ALC287_FIXUP_LEGION_16ACHG6,
        ALC287_FIXUP_CS35L41_I2C_2,
+       ALC245_FIXUP_CS35L41_SPI_2,
+       ALC245_FIXUP_CS35L41_SPI_4,
+       ALC245_FIXUP_CS35L41_SPI_4_HP_GPIO_LED,
        ALC285_FIXUP_HP_SPEAKERS_MICMUTE_LED,
 };
 
@@ -8699,6 +8763,20 @@ static const struct hda_fixup alc269_fixups[] = {
                .type = HDA_FIXUP_FUNC,
                .v.func = cs35l41_fixup_i2c_two,
        },
+       [ALC245_FIXUP_CS35L41_SPI_2] = {
+               .type = HDA_FIXUP_FUNC,
+               .v.func = cs35l41_fixup_spi_two,
+       },
+       [ALC245_FIXUP_CS35L41_SPI_4] = {
+               .type = HDA_FIXUP_FUNC,
+               .v.func = cs35l41_fixup_spi_four,
+       },
+       [ALC245_FIXUP_CS35L41_SPI_4_HP_GPIO_LED] = {
+               .type = HDA_FIXUP_FUNC,
+               .v.func = cs35l41_fixup_spi_four,
+               .chained = true,
+               .chain_id = ALC285_FIXUP_HP_GPIO_LED,
+       },
        [ALC285_FIXUP_HP_SPEAKERS_MICMUTE_LED] = {
                .type = HDA_FIXUP_VERBS,
                .v.verbs = (const struct hda_verb[]) {
@@ -8926,7 +9004,25 @@ static const struct snd_pci_quirk alc269_fixup_tbl[] = {
        SND_PCI_QUIRK(0x103c, 0x8896, "HP EliteBook 855 G8 Notebook PC", ALC285_FIXUP_HP_MUTE_LED),
        SND_PCI_QUIRK(0x103c, 0x8898, "HP EliteBook 845 G8 Notebook PC", ALC285_FIXUP_HP_LIMIT_INT_MIC_BOOST),
        SND_PCI_QUIRK(0x103c, 0x88d0, "HP Pavilion 15-eh1xxx (mainboard 88D0)", ALC287_FIXUP_HP_GPIO_LED),
-       SND_PCI_QUIRK(0x103c, 0x89c3, "HP", ALC285_FIXUP_HP_GPIO_LED),
+       SND_PCI_QUIRK(0x103c, 0x896e, "HP EliteBook x360 830 G9", ALC245_FIXUP_CS35L41_SPI_2),
+       SND_PCI_QUIRK(0x103c, 0x8971, "HP EliteBook 830 G9", ALC245_FIXUP_CS35L41_SPI_2),
+       SND_PCI_QUIRK(0x103c, 0x8972, "HP EliteBook 840 G9", ALC245_FIXUP_CS35L41_SPI_2),
+       SND_PCI_QUIRK(0x103c, 0x8973, "HP EliteBook 860 G9", ALC245_FIXUP_CS35L41_SPI_2),
+       SND_PCI_QUIRK(0x103c, 0x8974, "HP EliteBook 840 Aero G9", ALC245_FIXUP_CS35L41_SPI_2),
+       SND_PCI_QUIRK(0x103c, 0x8975, "HP EliteBook x360 840 Aero G9", ALC245_FIXUP_CS35L41_SPI_2),
+       SND_PCI_QUIRK(0x103c, 0x8981, "HP Elite Dragonfly G3", ALC245_FIXUP_CS35L41_SPI_4),
+       SND_PCI_QUIRK(0x103c, 0x898e, "HP EliteBook 835 G9", ALC287_FIXUP_CS35L41_I2C_2),
+       SND_PCI_QUIRK(0x103c, 0x898f, "HP EliteBook 835 G9", ALC287_FIXUP_CS35L41_I2C_2),
+       SND_PCI_QUIRK(0x103c, 0x8991, "HP EliteBook 845 G9", ALC287_FIXUP_CS35L41_I2C_2),
+       SND_PCI_QUIRK(0x103c, 0x8992, "HP EliteBook 845 G9", ALC287_FIXUP_CS35L41_I2C_2),
+       SND_PCI_QUIRK(0x103c, 0x8994, "HP EliteBook 855 G9", ALC287_FIXUP_CS35L41_I2C_2),
+       SND_PCI_QUIRK(0x103c, 0x8995, "HP EliteBook 855 G9", ALC287_FIXUP_CS35L41_I2C_2),
+       SND_PCI_QUIRK(0x103c, 0x89a4, "HP ProBook 440 G9", ALC236_FIXUP_HP_GPIO_LED),
+       SND_PCI_QUIRK(0x103c, 0x89a6, "HP ProBook 450 G9", ALC236_FIXUP_HP_GPIO_LED),
+       SND_PCI_QUIRK(0x103c, 0x89ac, "HP EliteBook 640 G9", ALC236_FIXUP_HP_GPIO_LED),
+       SND_PCI_QUIRK(0x103c, 0x89ae, "HP EliteBook 650 G9", ALC236_FIXUP_HP_GPIO_LED),
+       SND_PCI_QUIRK(0x103c, 0x89c3, "Zbook Studio G9", ALC245_FIXUP_CS35L41_SPI_4_HP_GPIO_LED),
+       SND_PCI_QUIRK(0x103c, 0x89c6, "Zbook Fury 17 G9", ALC245_FIXUP_CS35L41_SPI_2),
        SND_PCI_QUIRK(0x103c, 0x89ca, "HP", ALC236_FIXUP_HP_MUTE_LED_MICMUTE_VREF),
        SND_PCI_QUIRK(0x1043, 0x103e, "ASUS X540SA", ALC256_FIXUP_ASUS_MIC),
        SND_PCI_QUIRK(0x1043, 0x103f, "ASUS TX300", ALC282_FIXUP_ASUS_TX300),
@@ -8969,6 +9065,8 @@ static const struct snd_pci_quirk alc269_fixup_tbl[] = {
        SND_PCI_QUIRK(0x1043, 0x1e51, "ASUS Zephyrus M15", ALC294_FIXUP_ASUS_GU502_PINS),
        SND_PCI_QUIRK(0x1043, 0x1e8e, "ASUS Zephyrus G15", ALC289_FIXUP_ASUS_GA401),
        SND_PCI_QUIRK(0x1043, 0x1f11, "ASUS Zephyrus G14", ALC289_FIXUP_ASUS_GA401),
+       SND_PCI_QUIRK(0x1043, 0x1d42, "ASUS Zephyrus G14 2022", ALC289_FIXUP_ASUS_GA401),
+       SND_PCI_QUIRK(0x1043, 0x16b2, "ASUS GU603", ALC289_FIXUP_ASUS_GA401),
        SND_PCI_QUIRK(0x1043, 0x3030, "ASUS ZN270IE", ALC256_FIXUP_ASUS_AIO_GPIO2),
        SND_PCI_QUIRK(0x1043, 0x831a, "ASUS P901", ALC269_FIXUP_STEREO_DMIC),
        SND_PCI_QUIRK(0x1043, 0x834a, "ASUS S101", ALC269_FIXUP_STEREO_DMIC),
@@ -9051,6 +9149,8 @@ static const struct snd_pci_quirk alc269_fixup_tbl[] = {
        SND_PCI_QUIRK(0x1558, 0x8561, "Clevo NH[57][0-9][ER][ACDH]Q", ALC269_FIXUP_HEADSET_MIC),
        SND_PCI_QUIRK(0x1558, 0x8562, "Clevo NH[57][0-9]RZ[Q]", ALC269_FIXUP_DMIC),
        SND_PCI_QUIRK(0x1558, 0x8668, "Clevo NP50B[BE]", ALC293_FIXUP_SYSTEM76_MIC_NO_PRESENCE),
+       SND_PCI_QUIRK(0x1558, 0x866d, "Clevo NP5[05]PN[HJK]", ALC256_FIXUP_SYSTEM76_MIC_NO_PRESENCE),
+       SND_PCI_QUIRK(0x1558, 0x867d, "Clevo NP7[01]PN[HJK]", ALC256_FIXUP_SYSTEM76_MIC_NO_PRESENCE),
        SND_PCI_QUIRK(0x1558, 0x8680, "Clevo NJ50LU", ALC293_FIXUP_SYSTEM76_MIC_NO_PRESENCE),
        SND_PCI_QUIRK(0x1558, 0x8686, "Clevo NH50[CZ]U", ALC256_FIXUP_MIC_NO_PRESENCE_AND_RESUME),
        SND_PCI_QUIRK(0x1558, 0x8a20, "Clevo NH55DCQ-Y", ALC293_FIXUP_SYSTEM76_MIC_NO_PRESENCE),
@@ -9127,6 +9227,7 @@ static const struct snd_pci_quirk alc269_fixup_tbl[] = {
        SND_PCI_QUIRK(0x17aa, 0x3824, "Legion Y9000X 2020", ALC285_FIXUP_LEGION_Y9000X_SPEAKERS),
        SND_PCI_QUIRK(0x17aa, 0x3827, "Ideapad S740", ALC285_FIXUP_IDEAPAD_S740_COEF),
        SND_PCI_QUIRK(0x17aa, 0x3834, "Lenovo IdeaPad Slim 9i 14ITL5", ALC287_FIXUP_YOGA7_14ITL_SPEAKERS),
+       SND_PCI_QUIRK(0x17aa, 0x383d, "Legion Y9000X 2019", ALC285_FIXUP_LEGION_Y9000X_SPEAKERS),
        SND_PCI_QUIRK(0x17aa, 0x3843, "Yoga 9i", ALC287_FIXUP_IDEAPAD_BASS_SPK_AMP),
        SND_PCI_QUIRK(0x17aa, 0x3847, "Legion 7 16ACHG6", ALC287_FIXUP_LEGION_16ACHG6),
        SND_PCI_QUIRK(0x17aa, 0x384a, "Lenovo Yoga 7 15ITL5", ALC287_FIXUP_YOGA7_14ITL_SPEAKERS),
@@ -11014,6 +11115,7 @@ static const struct snd_pci_quirk alc662_fixup_tbl[] = {
        SND_PCI_QUIRK(0x1028, 0x069f, "Dell", ALC668_FIXUP_DELL_MIC_NO_PRESENCE),
        SND_PCI_QUIRK(0x103c, 0x1632, "HP RP5800", ALC662_FIXUP_HP_RP5800),
        SND_PCI_QUIRK(0x103c, 0x873e, "HP", ALC671_FIXUP_HP_HEADSET_MIC2),
+       SND_PCI_QUIRK(0x103c, 0x885f, "HP 288 Pro G8", ALC671_FIXUP_HP_HEADSET_MIC2),
        SND_PCI_QUIRK(0x1043, 0x1080, "Asus UX501VW", ALC668_FIXUP_HEADSET_MODE),
        SND_PCI_QUIRK(0x1043, 0x11cd, "Asus N550", ALC662_FIXUP_ASUS_Nx50),
        SND_PCI_QUIRK(0x1043, 0x129d, "Asus N750", ALC662_FIXUP_ASUS_Nx50),
index e2c8f14..6b16248 100644 (file)
@@ -121,6 +121,8 @@ int lola_init_mixer_widget(struct lola *chip, int nid)
 
        /* reserve memory to copy mixer data for sleep mode transitions */
        chip->mixer.array_saved = vmalloc(sizeof(struct lola_mixer_array));
+       if (!chip->mixer.array_saved)
+               return -ENOMEM;
 
        /* mixer matrix sources are physical input data and play streams */
        chip->mixer.src_stream_outs = chip->pcm[PLAY].num_streams;
index 755215d..caa202f 100644 (file)
@@ -329,11 +329,11 @@ static const struct snd_soc_dapm_route rt1019_map_lr[] = {
 
 static struct snd_soc_codec_conf rt1019_conf[] = {
        {
-                .dlc = COMP_CODEC_CONF("i2c-10EC1019:00"),
+                .dlc = COMP_CODEC_CONF("i2c-10EC1019:01"),
                 .name_prefix = "Left",
        },
        {
-                .dlc = COMP_CODEC_CONF("i2c-10EC1019:01"),
+                .dlc = COMP_CODEC_CONF("i2c-10EC1019:00"),
                 .name_prefix = "Right",
        },
 };
index fd62998..c855f50 100644 (file)
@@ -21,7 +21,6 @@
 #include <linux/gpio/consumer.h>
 
 #define EN_SPKR_GPIO_GB                0x11F
-#define EN_SPKR_GPIO_NK                0x146
 #define EN_SPKR_GPIO_NONE      -EINVAL
 
 enum be_id {
index c1d9650..3346677 100644 (file)
@@ -37,7 +37,7 @@ static struct acp_card_drvdata sof_rt5682_max_data = {
        .hs_codec_id = RT5682,
        .amp_codec_id = MAX98360A,
        .dmic_codec_id = DMIC,
-       .gpio_spkr_en = EN_SPKR_GPIO_NK,
+       .gpio_spkr_en = EN_SPKR_GPIO_NONE,
 };
 
 static struct acp_card_drvdata sof_rt5682s_rt1019_data = {
@@ -56,7 +56,7 @@ static struct acp_card_drvdata sof_rt5682s_max_data = {
        .hs_codec_id = RT5682S,
        .amp_codec_id = MAX98360A,
        .dmic_codec_id = DMIC,
-       .gpio_spkr_en = EN_SPKR_GPIO_NK,
+       .gpio_spkr_en = EN_SPKR_GPIO_NONE,
 };
 
 static const struct snd_kcontrol_new acp_controls[] = {
index 002db39..59a98f8 100644 (file)
@@ -88,10 +88,9 @@ static int acp5x_i2s_hwparams(struct snd_pcm_substream *substream,
        struct snd_soc_card *card;
        struct acp5x_platform_info *pinfo;
        struct i2s_dev_data *adata;
-       union acp_i2stdm_mstrclkgen mclkgen;
 
        u32 val;
-       u32 reg_val, frmt_reg, master_reg;
+       u32 reg_val, frmt_reg;
        u32 lrclk_div_val, bclk_div_val;
 
        lrclk_div_val = 0;
@@ -160,20 +159,6 @@ static int acp5x_i2s_hwparams(struct snd_pcm_substream *substream,
        acp_writel(val, rtd->acp5x_base + reg_val);
 
        if (adata->master_mode) {
-               switch (rtd->i2s_instance) {
-               case I2S_HS_INSTANCE:
-                       master_reg = ACP_I2STDM2_MSTRCLKGEN;
-                       break;
-               case I2S_SP_INSTANCE:
-               default:
-                       master_reg = ACP_I2STDM0_MSTRCLKGEN;
-                       break;
-               }
-               mclkgen.bits.i2stdm_master_mode = 0x1;
-               if (adata->tdm_mode)
-                       mclkgen.bits.i2stdm_format_mode = 0x01;
-               else
-                       mclkgen.bits.i2stdm_format_mode = 0x0;
                switch (params_format(params)) {
                case SNDRV_PCM_FORMAT_S16_LE:
                        switch (params_rate(params)) {
@@ -238,9 +223,8 @@ static int acp5x_i2s_hwparams(struct snd_pcm_substream *substream,
                default:
                        return -EINVAL;
                }
-               mclkgen.bits.i2stdm_bclk_div_val = bclk_div_val;
-               mclkgen.bits.i2stdm_lrclk_div_val = lrclk_div_val;
-               acp_writel(mclkgen.u32_all, rtd->acp5x_base + master_reg);
+               rtd->lrclk_div = lrclk_div_val;
+               rtd->bclk_div = bclk_div_val;
        }
        return 0;
 }
@@ -249,9 +233,11 @@ static int acp5x_i2s_trigger(struct snd_pcm_substream *substream,
                             int cmd, struct snd_soc_dai *dai)
 {
        struct i2s_stream_instance *rtd;
+       struct i2s_dev_data *adata;
        u32 ret, val, period_bytes, reg_val, ier_val, water_val;
        u32 buf_size, buf_reg;
 
+       adata = snd_soc_dai_get_drvdata(dai);
        rtd = substream->runtime->private_data;
        period_bytes = frames_to_bytes(substream->runtime,
                                       substream->runtime->period_size);
@@ -300,6 +286,8 @@ static int acp5x_i2s_trigger(struct snd_pcm_substream *substream,
                }
                acp_writel(period_bytes, rtd->acp5x_base + water_val);
                acp_writel(buf_size, rtd->acp5x_base + buf_reg);
+               if (adata->master_mode)
+                       acp5x_set_i2s_clk(adata, rtd);
                val = acp_readl(rtd->acp5x_base + reg_val);
                val = val | BIT(0);
                acp_writel(val, rtd->acp5x_base + reg_val);
index 28238af..1551546 100644 (file)
@@ -33,6 +33,8 @@
 #define DUAL_CHANNEL           2
 #define ACP5X_NUVOTON_CODEC_DAI        "nau8821-hifi"
 #define VG_JUPITER 1
+#define ACP5X_NUVOTON_BCLK 3072000
+#define ACP5X_NAU8821_FREQ_OUT 12288000
 
 static unsigned long acp5x_machine_id;
 static struct snd_soc_jack vg_headset;
@@ -98,6 +100,13 @@ static const struct snd_pcm_hw_constraint_list constraints_channels = {
        .mask = 0,
 };
 
+static const unsigned int acp5x_nau8821_format[] = {32};
+
+static struct snd_pcm_hw_constraint_list constraints_sample_bits = {
+       .list = acp5x_nau8821_format,
+       .count = ARRAY_SIZE(acp5x_nau8821_format),
+};
+
 static int acp5x_8821_startup(struct snd_pcm_substream *substream)
 {
        struct snd_pcm_runtime *runtime = substream->runtime;
@@ -113,6 +122,9 @@ static int acp5x_8821_startup(struct snd_pcm_substream *substream)
                                   &constraints_channels);
        snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_RATE,
                                   &constraints_rates);
+       snd_pcm_hw_constraint_list(substream->runtime, 0,
+                                  SNDRV_PCM_HW_PARAM_SAMPLE_BITS,
+                                  &constraints_sample_bits);
        return 0;
 }
 
@@ -275,6 +287,15 @@ static int platform_clock_control(struct snd_soc_dapm_widget *w,
                        dev_err(card->dev, "set sysclk err = %d\n", ret);
                        return -EIO;
                }
+       } else {
+               ret = snd_soc_dai_set_sysclk(codec_dai, NAU8821_CLK_FLL_BLK, 0,
+                                            SND_SOC_CLOCK_IN);
+               if (ret < 0)
+                       dev_err(codec_dai->dev, "can't set BLK clock %d\n", ret);
+               ret = snd_soc_dai_set_pll(codec_dai, 0, 0, ACP5X_NUVOTON_BCLK,
+                                         ACP5X_NAU8821_FREQ_OUT);
+               if (ret < 0)
+                       dev_err(codec_dai->dev, "can't set FLL: %d\n", ret);
        }
        return ret;
 }
@@ -290,7 +311,7 @@ static const struct snd_soc_dapm_widget acp5x_8821_widgets[] = {
        SND_SOC_DAPM_MIC("Headset Mic", NULL),
        SND_SOC_DAPM_MIC("Int Mic", NULL),
        SND_SOC_DAPM_SUPPLY("Platform Clock", SND_SOC_NOPM, 0, 0,
-                           platform_clock_control, SND_SOC_DAPM_POST_PMD),
+                           platform_clock_control, SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
 };
 
 static const struct snd_soc_dapm_route acp5x_8821_audio_route[] = {
index 3d4ce32..bd9f1c5 100644 (file)
@@ -105,6 +105,8 @@ struct i2s_stream_instance {
        dma_addr_t dma_addr;
        u64 bytescount;
        void __iomem *acp5x_base;
+       u32 lrclk_div;
+       u32 bclk_div;
 };
 
 union acp_dma_count {
@@ -191,3 +193,30 @@ static inline u64 acp_get_byte_count(struct i2s_stream_instance *rtd,
        }
        return byte_count.bytescount;
 }
+
+static inline void acp5x_set_i2s_clk(struct i2s_dev_data *adata,
+                                    struct i2s_stream_instance *rtd)
+{
+       union acp_i2stdm_mstrclkgen mclkgen;
+       u32 master_reg;
+
+       switch (rtd->i2s_instance) {
+       case I2S_HS_INSTANCE:
+               master_reg = ACP_I2STDM2_MSTRCLKGEN;
+               break;
+       case I2S_SP_INSTANCE:
+       default:
+               master_reg = ACP_I2STDM0_MSTRCLKGEN;
+               break;
+       }
+
+       mclkgen.bits.i2stdm_master_mode = 0x1;
+       if (adata->tdm_mode)
+               mclkgen.bits.i2stdm_format_mode = 0x01;
+       else
+               mclkgen.bits.i2stdm_format_mode = 0x00;
+
+       mclkgen.bits.i2stdm_bclk_div_val = rtd->bclk_div;
+       mclkgen.bits.i2stdm_lrclk_div_val = rtd->lrclk_div;
+       acp_writel(mclkgen.u32_all, rtd->acp5x_base + master_reg);
+}
index 2b6b9ed..e0df17c 100644 (file)
@@ -92,12 +92,14 @@ static int acp5x_init(void __iomem *acp5x_base)
                pr_err("ACP5x power on failed\n");
                return ret;
        }
+       acp_writel(0x01, acp5x_base + ACP_CONTROL);
        /* Reset */
        ret = acp5x_reset(acp5x_base);
        if (ret) {
                pr_err("ACP5x reset failed\n");
                return ret;
        }
+       acp_writel(0x03, acp5x_base + ACP_CLKMUX_SEL);
        acp5x_enable_interrupts(acp5x_base);
        return 0;
 }
@@ -113,6 +115,8 @@ static int acp5x_deinit(void __iomem *acp5x_base)
                pr_err("ACP5x reset failed\n");
                return ret;
        }
+       acp_writel(0x00, acp5x_base + ACP_CLKMUX_SEL);
+       acp_writel(0x00, acp5x_base + ACP_CONTROL);
        return 0;
 }
 
index e10d127..6b784a6 100644 (file)
@@ -573,7 +573,7 @@ static int cs35l41_main_amp_event(struct snd_soc_dapm_widget *w,
        int ret = 0;
 
        switch (event) {
-       case SND_SOC_DAPM_POST_PMU:
+       case SND_SOC_DAPM_PRE_PMU:
                regmap_multi_reg_write_bypassed(cs35l41->regmap,
                                                cs35l41_pup_patch,
                                                ARRAY_SIZE(cs35l41_pup_patch));
@@ -649,7 +649,7 @@ static const struct snd_soc_dapm_widget cs35l41_dapm_widgets[] = {
 
        SND_SOC_DAPM_OUT_DRV_E("Main AMP", CS35L41_PWR_CTRL2, 0, 0, NULL, 0,
                               cs35l41_main_amp_event,
-                              SND_SOC_DAPM_POST_PMD |  SND_SOC_DAPM_POST_PMU),
+                              SND_SOC_DAPM_POST_PMD |  SND_SOC_DAPM_PRE_PMU),
 
        SND_SOC_DAPM_MUX("ASP TX1 Source", SND_SOC_NOPM, 0, 0, &asp_tx1_mux),
        SND_SOC_DAPM_MUX("ASP TX2 Source", SND_SOC_NOPM, 0, 0, &asp_tx2_mux),
@@ -1035,8 +1035,8 @@ static int cs35l41_irq_gpio_config(struct cs35l41_private *cs35l41)
 
        regmap_update_bits(cs35l41->regmap, CS35L41_GPIO2_CTRL1,
                           CS35L41_GPIO_POL_MASK | CS35L41_GPIO_DIR_MASK,
-                          irq_gpio_cfg1->irq_pol_inv << CS35L41_GPIO_POL_SHIFT |
-                          !irq_gpio_cfg1->irq_out_en << CS35L41_GPIO_DIR_SHIFT);
+                          irq_gpio_cfg2->irq_pol_inv << CS35L41_GPIO_POL_SHIFT |
+                          !irq_gpio_cfg2->irq_out_en << CS35L41_GPIO_DIR_SHIFT);
 
        regmap_update_bits(cs35l41->regmap, CS35L41_GPIO_PAD_CONTROL,
                           CS35L41_GPIO1_CTRL_MASK | CS35L41_GPIO2_CTRL_MASK,
index 4aaee18..4415fb3 100644 (file)
@@ -150,7 +150,6 @@ static const struct snd_kcontrol_new cs4265_snd_controls[] = {
        SOC_SINGLE("E to F Buffer Disable Switch", CS4265_SPDIF_CTL1,
                                6, 1, 0),
        SOC_ENUM("C Data Access", cam_mode_enum),
-       SOC_SINGLE("SPDIF Switch", CS4265_SPDIF_CTL2, 5, 1, 1),
        SOC_SINGLE("Validity Bit Control Switch", CS4265_SPDIF_CTL2,
                                3, 1, 0),
        SOC_ENUM("SPDIF Mono/Stereo", spdif_mono_stereo_enum),
@@ -186,7 +185,7 @@ static const struct snd_soc_dapm_widget cs4265_dapm_widgets[] = {
 
        SND_SOC_DAPM_SWITCH("Loopback", SND_SOC_NOPM, 0, 0,
                        &loopback_ctl),
-       SND_SOC_DAPM_SWITCH("SPDIF", SND_SOC_NOPM, 0, 0,
+       SND_SOC_DAPM_SWITCH("SPDIF", CS4265_SPDIF_CTL2, 5, 1,
                        &spdif_switch),
        SND_SOC_DAPM_SWITCH("DAC", CS4265_PWRCTL, 1, 1,
                        &dac_switch),
index 402e049..a9f61c7 100644 (file)
@@ -418,7 +418,7 @@ static int hdac_hda_codec_probe(struct snd_soc_component *component)
                                       HDA_CODEC_IDX_CONTROLLER, true);
 
        ret = snd_hda_codec_device_new(hcodec->bus, component->card->snd_card,
-                                      hdev->addr, hcodec);
+                                      hdev->addr, hcodec, true);
        if (ret < 0) {
                dev_err(&hdev->dev, "failed to create hda codec %d\n", ret);
                goto error_no_pm;
index b61f980..b07607a 100644 (file)
@@ -277,7 +277,7 @@ struct hdmi_codec_priv {
        bool busy;
        struct snd_soc_jack *jack;
        unsigned int jack_status;
-       u8 iec_status[5];
+       u8 iec_status[AES_IEC958_STATUS_SIZE];
 };
 
 static const struct snd_soc_dapm_widget hdmi_widgets[] = {
index fc1d225..6884ae5 100644 (file)
@@ -2046,6 +2046,10 @@ static int rx_macro_load_compander_coeff(struct snd_soc_component *component,
        int i;
        int hph_pwr_mode;
 
+       /* AUX does not have compander */
+       if (comp == INTERP_AUX)
+               return 0;
+
        if (!rx->comp_enabled[comp])
                return 0;
 
@@ -2275,7 +2279,7 @@ static int rx_macro_mux_get(struct snd_kcontrol *kcontrol,
        struct snd_soc_component *component = snd_soc_dapm_to_component(widget->dapm);
        struct rx_macro *rx = snd_soc_component_get_drvdata(component);
 
-       ucontrol->value.integer.value[0] =
+       ucontrol->value.enumerated.item[0] =
                        rx->rx_port_value[widget->shift];
        return 0;
 }
@@ -2287,7 +2291,7 @@ static int rx_macro_mux_put(struct snd_kcontrol *kcontrol,
        struct snd_soc_component *component = snd_soc_dapm_to_component(widget->dapm);
        struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
        struct snd_soc_dapm_update *update = NULL;
-       u32 rx_port_value = ucontrol->value.integer.value[0];
+       u32 rx_port_value = ucontrol->value.enumerated.item[0];
        u32 aif_rst;
        struct rx_macro *rx = snd_soc_component_get_drvdata(component);
 
@@ -2399,7 +2403,7 @@ static int rx_macro_get_hph_pwr_mode(struct snd_kcontrol *kcontrol,
        struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol);
        struct rx_macro *rx = snd_soc_component_get_drvdata(component);
 
-       ucontrol->value.integer.value[0] = rx->hph_pwr_mode;
+       ucontrol->value.enumerated.item[0] = rx->hph_pwr_mode;
        return 0;
 }
 
@@ -2409,7 +2413,7 @@ static int rx_macro_put_hph_pwr_mode(struct snd_kcontrol *kcontrol,
        struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol);
        struct rx_macro *rx = snd_soc_component_get_drvdata(component);
 
-       rx->hph_pwr_mode = ucontrol->value.integer.value[0];
+       rx->hph_pwr_mode = ucontrol->value.enumerated.item[0];
        return 0;
 }
 
@@ -2695,8 +2699,8 @@ static uint32_t get_iir_band_coeff(struct snd_soc_component *component,
        int reg, b2_reg;
 
        /* Address does not automatically update if reading */
-       reg = CDC_RX_SIDETONE_IIR0_IIR_COEF_B1_CTL + 16 * iir_idx;
-       b2_reg = CDC_RX_SIDETONE_IIR0_IIR_COEF_B2_CTL + 16 * iir_idx;
+       reg = CDC_RX_SIDETONE_IIR0_IIR_COEF_B1_CTL + 0x80 * iir_idx;
+       b2_reg = CDC_RX_SIDETONE_IIR0_IIR_COEF_B2_CTL + 0x80 * iir_idx;
 
        snd_soc_component_write(component, reg,
                                ((band_idx * BAND_MAX + coeff_idx) *
@@ -2725,7 +2729,7 @@ static uint32_t get_iir_band_coeff(struct snd_soc_component *component,
 static void set_iir_band_coeff(struct snd_soc_component *component,
                               int iir_idx, int band_idx, uint32_t value)
 {
-       int reg = CDC_RX_SIDETONE_IIR0_IIR_COEF_B2_CTL + 16 * iir_idx;
+       int reg = CDC_RX_SIDETONE_IIR0_IIR_COEF_B2_CTL + 0x80 * iir_idx;
 
        snd_soc_component_write(component, reg, (value & 0xFF));
        snd_soc_component_write(component, reg, (value >> 8) & 0xFF);
@@ -2746,7 +2750,7 @@ static int rx_macro_put_iir_band_audio_mixer(
        int iir_idx = ctl->iir_idx;
        int band_idx = ctl->band_idx;
        u32 coeff[BAND_MAX];
-       int reg = CDC_RX_SIDETONE_IIR0_IIR_COEF_B1_CTL + 16 * iir_idx;
+       int reg = CDC_RX_SIDETONE_IIR0_IIR_COEF_B1_CTL + 0x80 * iir_idx;
 
        memcpy(&coeff[0], ucontrol->value.bytes.data, params->max);
 
index 37674f6..f3cb596 100644 (file)
@@ -786,7 +786,7 @@ static int va_macro_dec_mode_get(struct snd_kcontrol *kcontrol,
        struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
        int path = e->shift_l;
 
-       ucontrol->value.integer.value[0] = va->dec_mode[path];
+       ucontrol->value.enumerated.item[0] = va->dec_mode[path];
 
        return 0;
 }
@@ -795,7 +795,7 @@ static int va_macro_dec_mode_put(struct snd_kcontrol *kcontrol,
                                 struct snd_ctl_elem_value *ucontrol)
 {
        struct snd_soc_component *comp = snd_soc_kcontrol_component(kcontrol);
-       int value = ucontrol->value.integer.value[0];
+       int value = ucontrol->value.enumerated.item[0];
        struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
        int path = e->shift_l;
        struct va_macro *va = snd_soc_component_get_drvdata(comp);
index 2de8183..d67dc27 100644 (file)
@@ -814,6 +814,7 @@ static const struct snd_soc_dai_ops nau8821_dai_ops = {
        .hw_params = nau8821_hw_params,
        .set_fmt = nau8821_set_dai_fmt,
        .mute_stream = nau8821_digital_mute,
+       .no_capture_mute = 1,
 };
 
 #define NAU8821_RATES SNDRV_PCM_RATE_8000_192000
index fb09715..5b12cbf 100644 (file)
@@ -1022,11 +1022,13 @@ static void rt5668_jack_detect_handler(struct work_struct *work)
                container_of(work, struct rt5668_priv, jack_detect_work.work);
        int val, btn_type;
 
-       while (!rt5668->component)
-               usleep_range(10000, 15000);
-
-       while (!rt5668->component->card->instantiated)
-               usleep_range(10000, 15000);
+       if (!rt5668->component || !rt5668->component->card ||
+           !rt5668->component->card->instantiated) {
+               /* card not yet ready, try later */
+               mod_delayed_work(system_power_efficient_wq,
+                                &rt5668->jack_detect_work, msecs_to_jiffies(15));
+               return;
+       }
 
        mutex_lock(&rt5668->calibrate_mutex);
 
index 20e0f90..20fc0f3 100644 (file)
@@ -59,18 +59,12 @@ static void rt5682_jd_check_handler(struct work_struct *work)
        struct rt5682_priv *rt5682 = container_of(work, struct rt5682_priv,
                jd_check_work.work);
 
-       if (snd_soc_component_read(rt5682->component, RT5682_AJD1_CTRL)
-               & RT5682_JDH_RS_MASK) {
+       if (snd_soc_component_read(rt5682->component, RT5682_AJD1_CTRL) & RT5682_JDH_RS_MASK)
                /* jack out */
-               rt5682->jack_type = rt5682_headset_detect(rt5682->component, 0);
-
-               snd_soc_jack_report(rt5682->hs_jack, rt5682->jack_type,
-                       SND_JACK_HEADSET |
-                       SND_JACK_BTN_0 | SND_JACK_BTN_1 |
-                       SND_JACK_BTN_2 | SND_JACK_BTN_3);
-       } else {
+               mod_delayed_work(system_power_efficient_wq,
+                                &rt5682->jack_detect_work, 0);
+       else
                schedule_delayed_work(&rt5682->jd_check_work, 500);
-       }
 }
 
 static irqreturn_t rt5682_irq(int irq, void *data)
@@ -198,7 +192,6 @@ static int rt5682_i2c_probe(struct i2c_client *i2c,
        }
 
        mutex_init(&rt5682->calibrate_mutex);
-       mutex_init(&rt5682->jdet_mutex);
        rt5682_calibrate(rt5682);
 
        rt5682_apply_patch_list(rt5682, &i2c->dev);
index 415ec56..be68d57 100644 (file)
@@ -922,15 +922,13 @@ static void rt5682_enable_push_button_irq(struct snd_soc_component *component,
  *
  * Returns detect status.
  */
-int rt5682_headset_detect(struct snd_soc_component *component, int jack_insert)
+static int rt5682_headset_detect(struct snd_soc_component *component, int jack_insert)
 {
        struct rt5682_priv *rt5682 = snd_soc_component_get_drvdata(component);
        struct snd_soc_dapm_context *dapm = &component->dapm;
        unsigned int val, count;
 
        if (jack_insert) {
-               snd_soc_dapm_mutex_lock(dapm);
-
                snd_soc_component_update_bits(component, RT5682_PWR_ANLG_1,
                        RT5682_PWR_VREF2 | RT5682_PWR_MB,
                        RT5682_PWR_VREF2 | RT5682_PWR_MB);
@@ -981,8 +979,6 @@ int rt5682_headset_detect(struct snd_soc_component *component, int jack_insert)
                snd_soc_component_update_bits(component, RT5682_MICBIAS_2,
                        RT5682_PWR_CLK25M_MASK | RT5682_PWR_CLK1M_MASK,
                        RT5682_PWR_CLK25M_PU | RT5682_PWR_CLK1M_PU);
-
-               snd_soc_dapm_mutex_unlock(dapm);
        } else {
                rt5682_enable_push_button_irq(component, false);
                snd_soc_component_update_bits(component, RT5682_CBJ_CTRL_1,
@@ -1011,7 +1007,6 @@ int rt5682_headset_detect(struct snd_soc_component *component, int jack_insert)
        dev_dbg(component->dev, "jack_type = %d\n", rt5682->jack_type);
        return rt5682->jack_type;
 }
-EXPORT_SYMBOL_GPL(rt5682_headset_detect);
 
 static int rt5682_set_jack_detect(struct snd_soc_component *component,
                struct snd_soc_jack *hs_jack, void *data)
@@ -1094,15 +1089,20 @@ void rt5682_jack_detect_handler(struct work_struct *work)
 {
        struct rt5682_priv *rt5682 =
                container_of(work, struct rt5682_priv, jack_detect_work.work);
+       struct snd_soc_dapm_context *dapm;
        int val, btn_type;
 
-       while (!rt5682->component)
-               usleep_range(10000, 15000);
+       if (!rt5682->component || !rt5682->component->card ||
+           !rt5682->component->card->instantiated) {
+               /* card not yet ready, try later */
+               mod_delayed_work(system_power_efficient_wq,
+                                &rt5682->jack_detect_work, msecs_to_jiffies(15));
+               return;
+       }
 
-       while (!rt5682->component->card->instantiated)
-               usleep_range(10000, 15000);
+       dapm = snd_soc_component_get_dapm(rt5682->component);
 
-       mutex_lock(&rt5682->jdet_mutex);
+       snd_soc_dapm_mutex_lock(dapm);
        mutex_lock(&rt5682->calibrate_mutex);
 
        val = snd_soc_component_read(rt5682->component, RT5682_AJD1_CTRL)
@@ -1162,6 +1162,9 @@ void rt5682_jack_detect_handler(struct work_struct *work)
                rt5682->irq_work_delay_time = 50;
        }
 
+       mutex_unlock(&rt5682->calibrate_mutex);
+       snd_soc_dapm_mutex_unlock(dapm);
+
        snd_soc_jack_report(rt5682->hs_jack, rt5682->jack_type,
                SND_JACK_HEADSET |
                SND_JACK_BTN_0 | SND_JACK_BTN_1 |
@@ -1174,9 +1177,6 @@ void rt5682_jack_detect_handler(struct work_struct *work)
                else
                        cancel_delayed_work_sync(&rt5682->jd_check_work);
        }
-
-       mutex_unlock(&rt5682->calibrate_mutex);
-       mutex_unlock(&rt5682->jdet_mutex);
 }
 EXPORT_SYMBOL_GPL(rt5682_jack_detect_handler);
 
@@ -1526,7 +1526,6 @@ static int rt5682_hp_event(struct snd_soc_dapm_widget *w,
 {
        struct snd_soc_component *component =
                snd_soc_dapm_to_component(w->dapm);
-       struct rt5682_priv *rt5682 = snd_soc_component_get_drvdata(component);
 
        switch (event) {
        case SND_SOC_DAPM_PRE_PMU:
@@ -1538,17 +1537,12 @@ static int rt5682_hp_event(struct snd_soc_dapm_widget *w,
                        RT5682_DEPOP_1, 0x60, 0x60);
                snd_soc_component_update_bits(component,
                        RT5682_DAC_ADC_DIG_VOL1, 0x00c0, 0x0080);
-
-               mutex_lock(&rt5682->jdet_mutex);
-
                snd_soc_component_update_bits(component, RT5682_HP_CTRL_2,
                        RT5682_HP_C2_DAC_L_EN | RT5682_HP_C2_DAC_R_EN,
                        RT5682_HP_C2_DAC_L_EN | RT5682_HP_C2_DAC_R_EN);
                usleep_range(5000, 10000);
                snd_soc_component_update_bits(component, RT5682_CHARGE_PUMP_1,
                        RT5682_CP_SW_SIZE_MASK, RT5682_CP_SW_SIZE_L);
-
-               mutex_unlock(&rt5682->jdet_mutex);
                break;
 
        case SND_SOC_DAPM_POST_PMD:
index c917c76..52ff0d9 100644 (file)
@@ -1463,7 +1463,6 @@ struct rt5682_priv {
 
        int jack_type;
        int irq_work_delay_time;
-       struct mutex jdet_mutex;
 };
 
 extern const char *rt5682_supply_names[RT5682_NUM_SUPPLIES];
@@ -1473,7 +1472,6 @@ int rt5682_sel_asrc_clk_src(struct snd_soc_component *component,
 
 void rt5682_apply_patch_list(struct rt5682_priv *rt5682, struct device *dev);
 
-int rt5682_headset_detect(struct snd_soc_component *component, int jack_insert);
 void rt5682_jack_detect_handler(struct work_struct *work);
 
 bool rt5682_volatile_register(struct device *dev, unsigned int reg);
index 8f3d59c..1cba8ec 100644 (file)
@@ -830,15 +830,20 @@ static void rt5682s_jack_detect_handler(struct work_struct *work)
 {
        struct rt5682s_priv *rt5682s =
                container_of(work, struct rt5682s_priv, jack_detect_work.work);
+       struct snd_soc_dapm_context *dapm;
        int val, btn_type;
 
-       while (!rt5682s->component)
-               usleep_range(10000, 15000);
+       if (!rt5682s->component || !rt5682s->component->card ||
+           !rt5682s->component->card->instantiated) {
+               /* card not yet ready, try later */
+               mod_delayed_work(system_power_efficient_wq,
+                                &rt5682s->jack_detect_work, msecs_to_jiffies(15));
+               return;
+       }
 
-       while (!rt5682s->component->card->instantiated)
-               usleep_range(10000, 15000);
+       dapm = snd_soc_component_get_dapm(rt5682s->component);
 
-       mutex_lock(&rt5682s->jdet_mutex);
+       snd_soc_dapm_mutex_lock(dapm);
        mutex_lock(&rt5682s->calibrate_mutex);
 
        val = snd_soc_component_read(rt5682s->component, RT5682S_AJD1_CTRL)
@@ -895,6 +900,9 @@ static void rt5682s_jack_detect_handler(struct work_struct *work)
                rt5682s->irq_work_delay_time = 50;
        }
 
+       mutex_unlock(&rt5682s->calibrate_mutex);
+       snd_soc_dapm_mutex_unlock(dapm);
+
        snd_soc_jack_report(rt5682s->hs_jack, rt5682s->jack_type,
                SND_JACK_HEADSET | SND_JACK_BTN_0 | SND_JACK_BTN_1 |
                SND_JACK_BTN_2 | SND_JACK_BTN_3);
@@ -904,9 +912,6 @@ static void rt5682s_jack_detect_handler(struct work_struct *work)
                schedule_delayed_work(&rt5682s->jd_check_work, 0);
        else
                cancel_delayed_work_sync(&rt5682s->jd_check_work);
-
-       mutex_unlock(&rt5682s->calibrate_mutex);
-       mutex_unlock(&rt5682s->jdet_mutex);
 }
 
 static void rt5682s_jd_check_handler(struct work_struct *work)
@@ -914,14 +919,9 @@ static void rt5682s_jd_check_handler(struct work_struct *work)
        struct rt5682s_priv *rt5682s =
                container_of(work, struct rt5682s_priv, jd_check_work.work);
 
-       if (snd_soc_component_read(rt5682s->component, RT5682S_AJD1_CTRL)
-               & RT5682S_JDH_RS_MASK) {
+       if (snd_soc_component_read(rt5682s->component, RT5682S_AJD1_CTRL) & RT5682S_JDH_RS_MASK) {
                /* jack out */
-               rt5682s->jack_type = rt5682s_headset_detect(rt5682s->component, 0);
-
-               snd_soc_jack_report(rt5682s->hs_jack, rt5682s->jack_type,
-                       SND_JACK_HEADSET | SND_JACK_BTN_0 | SND_JACK_BTN_1 |
-                       SND_JACK_BTN_2 | SND_JACK_BTN_3);
+               schedule_delayed_work(&rt5682s->jack_detect_work, 0);
        } else {
                schedule_delayed_work(&rt5682s->jd_check_work, 500);
        }
@@ -1329,7 +1329,6 @@ static int rt5682s_hp_amp_event(struct snd_soc_dapm_widget *w,
                struct snd_kcontrol *kcontrol, int event)
 {
        struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
-       struct rt5682s_priv *rt5682s = snd_soc_component_get_drvdata(component);
 
        switch (event) {
        case SND_SOC_DAPM_POST_PMU:
@@ -1345,8 +1344,6 @@ static int rt5682s_hp_amp_event(struct snd_soc_dapm_widget *w,
                snd_soc_component_write(component, RT5682S_BIAS_CUR_CTRL_11, 0x6666);
                snd_soc_component_write(component, RT5682S_BIAS_CUR_CTRL_12, 0xa82a);
 
-               mutex_lock(&rt5682s->jdet_mutex);
-
                snd_soc_component_update_bits(component, RT5682S_HP_CTRL_2,
                        RT5682S_HPO_L_PATH_MASK | RT5682S_HPO_R_PATH_MASK |
                        RT5682S_HPO_SEL_IP_EN_SW, RT5682S_HPO_L_PATH_EN |
@@ -1354,8 +1351,6 @@ static int rt5682s_hp_amp_event(struct snd_soc_dapm_widget *w,
                usleep_range(5000, 10000);
                snd_soc_component_update_bits(component, RT5682S_HP_AMP_DET_CTL_1,
                        RT5682S_CP_SW_SIZE_MASK, RT5682S_CP_SW_SIZE_L | RT5682S_CP_SW_SIZE_S);
-
-               mutex_unlock(&rt5682s->jdet_mutex);
                break;
 
        case SND_SOC_DAPM_POST_PMD:
@@ -3106,7 +3101,6 @@ static int rt5682s_i2c_probe(struct i2c_client *i2c,
 
        mutex_init(&rt5682s->calibrate_mutex);
        mutex_init(&rt5682s->sar_mutex);
-       mutex_init(&rt5682s->jdet_mutex);
        rt5682s_calibrate(rt5682s);
 
        regmap_update_bits(rt5682s->regmap, RT5682S_MICBIAS_2,
index 1bf2ef7..397a253 100644 (file)
@@ -1446,7 +1446,6 @@ struct rt5682s_priv {
        struct delayed_work jd_check_work;
        struct mutex calibrate_mutex;
        struct mutex sar_mutex;
-       struct mutex jdet_mutex;
 
 #ifdef CONFIG_COMMON_CLK
        struct clk_hw dai_clks_hw[RT5682S_DAI_NUM_CLKS];
index 6549e7f..c5ea3b1 100644 (file)
@@ -38,10 +38,12 @@ static void tas2770_reset(struct tas2770_priv *tas2770)
                gpiod_set_value_cansleep(tas2770->reset_gpio, 0);
                msleep(20);
                gpiod_set_value_cansleep(tas2770->reset_gpio, 1);
+               usleep_range(1000, 2000);
        }
 
        snd_soc_component_write(tas2770->component, TAS2770_SW_RST,
                TAS2770_RST);
+       usleep_range(1000, 2000);
 }
 
 static int tas2770_set_bias_level(struct snd_soc_component *component,
@@ -110,6 +112,7 @@ static int tas2770_codec_resume(struct snd_soc_component *component)
 
        if (tas2770->sdz_gpio) {
                gpiod_set_value_cansleep(tas2770->sdz_gpio, 1);
+               usleep_range(1000, 2000);
        } else {
                ret = snd_soc_component_update_bits(component, TAS2770_PWR_CTRL,
                                                    TAS2770_PWR_CTRL_MASK,
@@ -510,8 +513,10 @@ static int tas2770_codec_probe(struct snd_soc_component *component)
 
        tas2770->component = component;
 
-       if (tas2770->sdz_gpio)
+       if (tas2770->sdz_gpio) {
                gpiod_set_value_cansleep(tas2770->sdz_gpio, 1);
+               usleep_range(1000, 2000);
+       }
 
        tas2770_reset(tas2770);
 
index 1d12a3f..ae18982 100644 (file)
@@ -1250,6 +1250,7 @@ static int adc3xxx_set_dai_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt)
        struct adc3xxx *adc3xxx = snd_soc_component_get_drvdata(component);
        u8 clkdir = 0, format = 0;
        int master = 0;
+       int ret;
 
        /* set master/slave audio interface */
        switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
@@ -1300,10 +1301,13 @@ static int adc3xxx_set_dai_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt)
        adc3xxx->master = master;
 
        /* set clock direction and format */
-       return snd_soc_component_update_bits(component,
-                                            ADC3XXX_INTERFACE_CTRL_1,
-                                            ADC3XXX_CLKDIR_MASK | ADC3XXX_FORMAT_MASK,
-                                            clkdir | format);
+       ret = snd_soc_component_update_bits(component,
+                                           ADC3XXX_INTERFACE_CTRL_1,
+                                           ADC3XXX_CLKDIR_MASK | ADC3XXX_FORMAT_MASK,
+                                           clkdir | format);
+       if (ret < 0)
+               return ret;
+       return 0;
 }
 
 static const struct snd_soc_dai_ops adc3xxx_dai_ops = {
index acd344c..1e75e93 100644 (file)
@@ -3023,14 +3023,14 @@ static int wcd934x_hph_impedance_get(struct snd_kcontrol *kcontrol,
        return 0;
 }
 static const struct snd_kcontrol_new hph_type_detect_controls[] = {
-       SOC_SINGLE_EXT("HPH Type", 0, 0, UINT_MAX, 0,
+       SOC_SINGLE_EXT("HPH Type", 0, 0, WCD_MBHC_HPH_STEREO, 0,
                       wcd934x_get_hph_type, NULL),
 };
 
 static const struct snd_kcontrol_new impedance_detect_controls[] = {
-       SOC_SINGLE_EXT("HPHL Impedance", 0, 0, UINT_MAX, 0,
+       SOC_SINGLE_EXT("HPHL Impedance", 0, 0, INT_MAX, 0,
                       wcd934x_hph_impedance_get, NULL),
-       SOC_SINGLE_EXT("HPHR Impedance", 0, 1, UINT_MAX, 0,
+       SOC_SINGLE_EXT("HPHR Impedance", 0, 1, INT_MAX, 0,
                       wcd934x_hph_impedance_get, NULL),
 };
 
@@ -3308,13 +3308,16 @@ static int wcd934x_rx_hph_mode_put(struct snd_kcontrol *kc,
 
        mode_val = ucontrol->value.enumerated.item[0];
 
+       if (mode_val == wcd->hph_mode)
+               return 0;
+
        if (mode_val == 0) {
                dev_err(wcd->dev, "Invalid HPH Mode, default to ClSH HiFi\n");
                mode_val = CLS_H_LOHIFI;
        }
        wcd->hph_mode = mode_val;
 
-       return 0;
+       return 1;
 }
 
 static int slim_rx_mux_get(struct snd_kcontrol *kc,
index 88a39e1..782877d 100644 (file)
@@ -1434,14 +1434,10 @@ static int wcd938x_sdw_connect_port(struct wcd938x_sdw_ch_info *ch_info,
        return 0;
 }
 
-static int wcd938x_connect_port(struct wcd938x_sdw_priv *wcd, u8 ch_id, u8 enable)
+static int wcd938x_connect_port(struct wcd938x_sdw_priv *wcd, u8 port_num, u8 ch_id, u8 enable)
 {
-       u8 port_num;
-
-       port_num = wcd->ch_info[ch_id].port_num;
-
        return wcd938x_sdw_connect_port(&wcd->ch_info[ch_id],
-                                       &wcd->port_config[port_num],
+                                       &wcd->port_config[port_num - 1],
                                        enable);
 }
 
@@ -2510,7 +2506,7 @@ static int wcd938x_tx_mode_get(struct snd_kcontrol *kcontrol,
        struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
        int path = e->shift_l;
 
-       ucontrol->value.integer.value[0] = wcd938x->tx_mode[path];
+       ucontrol->value.enumerated.item[0] = wcd938x->tx_mode[path];
 
        return 0;
 }
@@ -2534,7 +2530,7 @@ static int wcd938x_rx_hph_mode_get(struct snd_kcontrol *kcontrol,
        struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol);
        struct wcd938x_priv *wcd938x = snd_soc_component_get_drvdata(component);
 
-       ucontrol->value.integer.value[0] = wcd938x->hph_mode;
+       ucontrol->value.enumerated.item[0] = wcd938x->hph_mode;
 
        return 0;
 }
@@ -2565,7 +2561,7 @@ static int wcd938x_ear_pa_put_gain(struct snd_kcontrol *kcontrol,
                                      WCD938X_EAR_GAIN_MASK,
                                      ucontrol->value.integer.value[0]);
 
-       return 0;
+       return 1;
 }
 
 static int wcd938x_get_compander(struct snd_kcontrol *kcontrol,
@@ -2595,6 +2591,7 @@ static int wcd938x_set_compander(struct snd_kcontrol *kcontrol,
        struct wcd938x_priv *wcd938x = snd_soc_component_get_drvdata(component);
        struct wcd938x_sdw_priv *wcd;
        int value = ucontrol->value.integer.value[0];
+       int portidx;
        struct soc_mixer_control *mc;
        bool hphr;
 
@@ -2608,12 +2605,14 @@ static int wcd938x_set_compander(struct snd_kcontrol *kcontrol,
        else
                wcd938x->comp1_enable = value;
 
+       portidx = wcd->ch_info[mc->reg].port_num;
+
        if (value)
-               wcd938x_connect_port(wcd, mc->reg, true);
+               wcd938x_connect_port(wcd, portidx, mc->reg, true);
        else
-               wcd938x_connect_port(wcd, mc->reg, false);
+               wcd938x_connect_port(wcd, portidx, mc->reg, false);
 
-       return 0;
+       return 1;
 }
 
 static int wcd938x_ldoh_get(struct snd_kcontrol *kcontrol,
@@ -2884,9 +2883,11 @@ static int wcd938x_get_swr_port(struct snd_kcontrol *kcontrol,
        struct wcd938x_sdw_priv *wcd;
        struct soc_mixer_control *mixer = (struct soc_mixer_control *)kcontrol->private_value;
        int dai_id = mixer->shift;
-       int portidx = mixer->reg;
+       int portidx, ch_idx = mixer->reg;
+
 
        wcd = wcd938x->sdw_priv[dai_id];
+       portidx = wcd->ch_info[ch_idx].port_num;
 
        ucontrol->value.integer.value[0] = wcd->port_enable[portidx];
 
@@ -2901,12 +2902,14 @@ static int wcd938x_set_swr_port(struct snd_kcontrol *kcontrol,
        struct wcd938x_sdw_priv *wcd;
        struct soc_mixer_control *mixer =
                (struct soc_mixer_control *)kcontrol->private_value;
-       int portidx = mixer->reg;
+       int ch_idx = mixer->reg;
+       int portidx;
        int dai_id = mixer->shift;
        bool enable;
 
        wcd = wcd938x->sdw_priv[dai_id];
 
+       portidx = wcd->ch_info[ch_idx].port_num;
        if (ucontrol->value.integer.value[0])
                enable = true;
        else
@@ -2914,9 +2917,9 @@ static int wcd938x_set_swr_port(struct snd_kcontrol *kcontrol,
 
        wcd->port_enable[portidx] = enable;
 
-       wcd938x_connect_port(wcd, portidx, enable);
+       wcd938x_connect_port(wcd, portidx, ch_idx, enable);
 
-       return 0;
+       return 1;
 
 }
 
@@ -3574,14 +3577,14 @@ static int wcd938x_hph_impedance_get(struct snd_kcontrol *kcontrol,
 }
 
 static const struct snd_kcontrol_new hph_type_detect_controls[] = {
-       SOC_SINGLE_EXT("HPH Type", 0, 0, UINT_MAX, 0,
+       SOC_SINGLE_EXT("HPH Type", 0, 0, WCD_MBHC_HPH_STEREO, 0,
                       wcd938x_get_hph_type, NULL),
 };
 
 static const struct snd_kcontrol_new impedance_detect_controls[] = {
-       SOC_SINGLE_EXT("HPHL Impedance", 0, 0, UINT_MAX, 0,
+       SOC_SINGLE_EXT("HPHL Impedance", 0, 0, INT_MAX, 0,
                       wcd938x_hph_impedance_get, NULL),
-       SOC_SINGLE_EXT("HPHR Impedance", 0, 1, UINT_MAX, 0,
+       SOC_SINGLE_EXT("HPHR Impedance", 0, 1, INT_MAX, 0,
                       wcd938x_hph_impedance_get, NULL),
 };
 
index 375cb14..e32c8de 100644 (file)
@@ -1528,7 +1528,8 @@ static int wm_adsp_buffer_parse_coeff(struct cs_dsp_coeff_ctl *cs_ctl)
        int ret, i;
 
        for (i = 0; i < 5; ++i) {
-               ret = cs_dsp_coeff_read_ctrl(cs_ctl, 0, &coeff_v1, sizeof(coeff_v1));
+               ret = cs_dsp_coeff_read_ctrl(cs_ctl, 0, &coeff_v1,
+                                            min(cs_ctl->len, sizeof(coeff_v1)));
                if (ret < 0)
                        return ret;
 
index af3c3b9..83b4a22 100644 (file)
@@ -93,16 +93,21 @@ static int pcm030_fabric_probe(struct platform_device *op)
                dev_err(&op->dev, "platform_device_alloc() failed\n");
 
        ret = platform_device_add(pdata->codec_device);
-       if (ret)
+       if (ret) {
                dev_err(&op->dev, "platform_device_add() failed: %d\n", ret);
+               platform_device_put(pdata->codec_device);
+       }
 
        ret = snd_soc_register_card(card);
-       if (ret)
+       if (ret) {
                dev_err(&op->dev, "snd_soc_register_card() failed: %d\n", ret);
+               platform_device_del(pdata->codec_device);
+               platform_device_put(pdata->codec_device);
+       }
 
        platform_set_drvdata(op, pdata);
-
        return ret;
+
 }
 
 static int pcm030_fabric_remove(struct platform_device *op)
index daa5188..db5a92b 100644 (file)
@@ -125,7 +125,7 @@ static const struct snd_soc_acpi_codecs rt5640_comp_ids = {
 
 static const struct snd_soc_acpi_codecs wm5102_comp_ids = {
        .num_codecs = 2,
-       .codecs = { "WM510204", "WM510205"},
+       .codecs = { "10WM5102", "WM510204", "WM510205"},
 };
 
 static const struct snd_soc_acpi_codecs da7213_comp_ids = {
index 148ddf4..aeca582 100644 (file)
@@ -952,6 +952,7 @@ static int skl_first_init(struct hdac_bus *bus)
        /* allow 64bit DMA address if supported by H/W */
        if (dma_set_mask_and_coherent(bus->dev, DMA_BIT_MASK(64)))
                dma_set_mask_and_coherent(bus->dev, DMA_BIT_MASK(32));
+       dma_set_max_seg_size(bus->dev, UINT_MAX);
 
        /* initialize streams */
        snd_hdac_ext_stream_init_all
index 6f58f24..74d62f3 100644 (file)
@@ -761,7 +761,7 @@ static int lpass_platform_pcmops_trigger(struct snd_soc_component *component,
                        return -EINVAL;
                }
 
-               ret = regmap_update_bits(map, reg_irqclr, val_irqclr, val_irqclr);
+               ret = regmap_write_bits(map, reg_irqclr, val_irqclr, val_irqclr);
                if (ret) {
                        dev_err(soc_runtime->dev, "error writing to irqclear reg: %d\n", ret);
                        return ret;
@@ -969,7 +969,7 @@ static irqreturn_t lpass_dma_interrupt_handler(
        return -EINVAL;
        }
        if (interrupts & LPAIF_IRQ_PER(chan)) {
-               rv = regmap_update_bits(map, reg, mask, (LPAIF_IRQ_PER(chan) | val));
+               rv = regmap_write_bits(map, reg, mask, (LPAIF_IRQ_PER(chan) | val));
                if (rv) {
                        dev_err(soc_runtime->dev,
                                "error writing to irqclear reg: %d\n", rv);
@@ -980,7 +980,7 @@ static irqreturn_t lpass_dma_interrupt_handler(
        }
 
        if (interrupts & LPAIF_IRQ_XRUN(chan)) {
-               rv = regmap_update_bits(map, reg, mask, (LPAIF_IRQ_XRUN(chan) | val));
+               rv = regmap_write_bits(map, reg, mask, (LPAIF_IRQ_XRUN(chan) | val));
                if (rv) {
                        dev_err(soc_runtime->dev,
                                "error writing to irqclear reg: %d\n", rv);
@@ -993,7 +993,7 @@ static irqreturn_t lpass_dma_interrupt_handler(
        }
 
        if (interrupts & LPAIF_IRQ_ERR(chan)) {
-               rv = regmap_update_bits(map, reg, mask, (LPAIF_IRQ_ERR(chan) | val));
+               rv = regmap_write_bits(map, reg, mask, (LPAIF_IRQ_ERR(chan) | val));
                if (rv) {
                        dev_err(soc_runtime->dev,
                                "error writing to irqclear reg: %d\n", rv);
index eb1c3ae..19c4a90 100644 (file)
@@ -308,8 +308,11 @@ static int q6apm_dai_close(struct snd_soc_component *component,
        struct snd_pcm_runtime *runtime = substream->runtime;
        struct q6apm_dai_rtd *prtd = runtime->private_data;
 
-       q6apm_graph_stop(prtd->graph);
-       q6apm_unmap_memory_regions(prtd->graph, substream->stream);
+       if (prtd->state) { /* only stop graph that is started */
+               q6apm_graph_stop(prtd->graph);
+               q6apm_unmap_memory_regions(prtd->graph, substream->stream);
+       }
+
        q6apm_graph_close(prtd->graph);
        prtd->graph = NULL;
        kfree(prtd);
index cbd7ea4..142476f 100644 (file)
@@ -55,16 +55,13 @@ EXPORT_SYMBOL_GPL(snd_soc_acpi_find_machine);
 static acpi_status snd_soc_acpi_find_package(acpi_handle handle, u32 level,
                                             void *context, void **ret)
 {
-       struct acpi_device *adev;
+       struct acpi_device *adev = acpi_fetch_acpi_dev(handle);
        acpi_status status;
        struct snd_soc_acpi_package_context *pkg_ctx = context;
 
        pkg_ctx->data_valid = false;
 
-       if (acpi_bus_get_device(handle, &adev))
-               return AE_OK;
-
-       if (adev->status.present && adev->status.functional) {
+       if (adev && adev->status.present && adev->status.functional) {
                struct acpi_buffer buffer = {ACPI_ALLOCATE_BUFFER, NULL};
                union acpi_object  *myobj = NULL;
 
index 08eaa9d..a0ca58b 100644 (file)
@@ -308,7 +308,7 @@ int snd_soc_put_volsw(struct snd_kcontrol *kcontrol,
        unsigned int sign_bit = mc->sign_bit;
        unsigned int mask = (1 << fls(max)) - 1;
        unsigned int invert = mc->invert;
-       int err;
+       int err, ret;
        bool type_2r = false;
        unsigned int val2 = 0;
        unsigned int val, val_mask;
@@ -316,13 +316,27 @@ int snd_soc_put_volsw(struct snd_kcontrol *kcontrol,
        if (sign_bit)
                mask = BIT(sign_bit + 1) - 1;
 
-       val = ((ucontrol->value.integer.value[0] + min) & mask);
+       if (ucontrol->value.integer.value[0] < 0)
+               return -EINVAL;
+       val = ucontrol->value.integer.value[0];
+       if (mc->platform_max && ((int)val + min) > mc->platform_max)
+               return -EINVAL;
+       if (val > max - min)
+               return -EINVAL;
+       val = (val + min) & mask;
        if (invert)
                val = max - val;
        val_mask = mask << shift;
        val = val << shift;
        if (snd_soc_volsw_is_stereo(mc)) {
-               val2 = ((ucontrol->value.integer.value[1] + min) & mask);
+               if (ucontrol->value.integer.value[1] < 0)
+                       return -EINVAL;
+               val2 = ucontrol->value.integer.value[1];
+               if (mc->platform_max && ((int)val2 + min) > mc->platform_max)
+                       return -EINVAL;
+               if (val2 > max - min)
+                       return -EINVAL;
+               val2 = (val2 + min) & mask;
                if (invert)
                        val2 = max - val2;
                if (reg == reg2) {
@@ -336,12 +350,18 @@ int snd_soc_put_volsw(struct snd_kcontrol *kcontrol,
        err = snd_soc_component_update_bits(component, reg, val_mask, val);
        if (err < 0)
                return err;
+       ret = err;
 
-       if (type_2r)
+       if (type_2r) {
                err = snd_soc_component_update_bits(component, reg2, val_mask,
-                       val2);
+                                                   val2);
+               /* Don't discard any error code or drop change flag */
+               if (ret == 0 || err < 0) {
+                       ret = err;
+               }
+       }
 
-       return err;
+       return ret;
 }
 EXPORT_SYMBOL_GPL(snd_soc_put_volsw);
 
@@ -407,15 +427,24 @@ int snd_soc_put_volsw_sx(struct snd_kcontrol *kcontrol,
        int min = mc->min;
        unsigned int mask = (1U << (fls(min + max) - 1)) - 1;
        int err = 0;
+       int ret;
        unsigned int val, val_mask;
 
+       if (ucontrol->value.integer.value[0] < 0)
+               return -EINVAL;
+       val = ucontrol->value.integer.value[0];
+       if (mc->platform_max && val > mc->platform_max)
+               return -EINVAL;
+       if (val > max - min)
+               return -EINVAL;
        val_mask = mask << shift;
-       val = (ucontrol->value.integer.value[0] + min) & mask;
+       val = (val + min) & mask;
        val = val << shift;
 
        err = snd_soc_component_update_bits(component, reg, val_mask, val);
        if (err < 0)
                return err;
+       ret = err;
 
        if (snd_soc_volsw_is_stereo(mc)) {
                unsigned int val2;
@@ -426,6 +455,11 @@ int snd_soc_put_volsw_sx(struct snd_kcontrol *kcontrol,
 
                err = snd_soc_component_update_bits(component, reg2, val_mask,
                        val2);
+
+               /* Don't discard any error code or drop change flag */
+               if (ret == 0 || err < 0) {
+                       ret = err;
+               }
        }
        return err;
 }
@@ -485,7 +519,7 @@ int snd_soc_put_volsw_range(struct snd_kcontrol *kcontrol,
        unsigned int mask = (1 << fls(max)) - 1;
        unsigned int invert = mc->invert;
        unsigned int val, val_mask;
-       int ret;
+       int err, ret;
 
        if (invert)
                val = (max - ucontrol->value.integer.value[0]) & mask;
@@ -494,9 +528,10 @@ int snd_soc_put_volsw_range(struct snd_kcontrol *kcontrol,
        val_mask = mask << shift;
        val = val << shift;
 
-       ret = snd_soc_component_update_bits(component, reg, val_mask, val);
-       if (ret < 0)
-               return ret;
+       err = snd_soc_component_update_bits(component, reg, val_mask, val);
+       if (err < 0)
+               return err;
+       ret = err;
 
        if (snd_soc_volsw_is_stereo(mc)) {
                if (invert)
@@ -506,8 +541,12 @@ int snd_soc_put_volsw_range(struct snd_kcontrol *kcontrol,
                val_mask = mask << shift;
                val = val << shift;
 
-               ret = snd_soc_component_update_bits(component, rreg, val_mask,
+               err = snd_soc_component_update_bits(component, rreg, val_mask,
                        val);
+               /* Don't discard any error code or drop change flag */
+               if (ret == 0 || err < 0) {
+                       ret = err;
+               }
        }
 
        return ret;
@@ -856,8 +895,11 @@ int snd_soc_put_xr_sx(struct snd_kcontrol *kcontrol,
        unsigned long mask = (1UL<<mc->nbits)-1;
        long max = mc->max;
        long val = ucontrol->value.integer.value[0];
+       int ret = 0;
        unsigned int i;
 
+       if (val < mc->min || val > mc->max)
+               return -EINVAL;
        if (invert)
                val = max - val;
        val &= mask;
@@ -868,9 +910,11 @@ int snd_soc_put_xr_sx(struct snd_kcontrol *kcontrol,
                                                        regmask, regval);
                if (err < 0)
                        return err;
+               if (err > 0)
+                       ret = err;
        }
 
-       return 0;
+       return ret;
 }
 EXPORT_SYMBOL_GPL(snd_soc_put_xr_sx);
 
index 7abfc48..9a95468 100644 (file)
@@ -46,8 +46,8 @@ static inline void snd_soc_dpcm_stream_lock_irq(struct snd_soc_pcm_runtime *rtd,
        snd_pcm_stream_lock_irq(snd_soc_dpcm_get_substream(rtd, stream));
 }
 
-#define snd_soc_dpcm_stream_lock_irqsave(rtd, stream, flags) \
-       snd_pcm_stream_lock_irqsave(snd_soc_dpcm_get_substream(rtd, stream), flags)
+#define snd_soc_dpcm_stream_lock_irqsave_nested(rtd, stream, flags) \
+       snd_pcm_stream_lock_irqsave_nested(snd_soc_dpcm_get_substream(rtd, stream), flags)
 
 static inline void snd_soc_dpcm_stream_unlock_irq(struct snd_soc_pcm_runtime *rtd,
                                                  int stream)
@@ -1268,6 +1268,7 @@ static void dpcm_be_reparent(struct snd_soc_pcm_runtime *fe,
 void dpcm_be_disconnect(struct snd_soc_pcm_runtime *fe, int stream)
 {
        struct snd_soc_dpcm *dpcm, *d;
+       LIST_HEAD(deleted_dpcms);
 
        snd_soc_dpcm_mutex_assert_held(fe);
 
@@ -1287,13 +1288,18 @@ void dpcm_be_disconnect(struct snd_soc_pcm_runtime *fe, int stream)
                /* BEs still alive need new FE */
                dpcm_be_reparent(fe, dpcm->be, stream);
 
-               dpcm_remove_debugfs_state(dpcm);
-
                list_del(&dpcm->list_be);
+               list_move(&dpcm->list_fe, &deleted_dpcms);
+       }
+       snd_soc_dpcm_stream_unlock_irq(fe, stream);
+
+       while (!list_empty(&deleted_dpcms)) {
+               dpcm = list_first_entry(&deleted_dpcms, struct snd_soc_dpcm,
+                                       list_fe);
                list_del(&dpcm->list_fe);
+               dpcm_remove_debugfs_state(dpcm);
                kfree(dpcm);
        }
-       snd_soc_dpcm_stream_unlock_irq(fe, stream);
 }
 
 /* get BE for DAI widget and stream */
@@ -2094,7 +2100,7 @@ int dpcm_be_dai_trigger(struct snd_soc_pcm_runtime *fe, int stream,
                be = dpcm->be;
                be_substream = snd_soc_dpcm_get_substream(be, stream);
 
-               snd_soc_dpcm_stream_lock_irqsave(be, stream, flags);
+               snd_soc_dpcm_stream_lock_irqsave_nested(be, stream, flags);
 
                /* is this op for this BE ? */
                if (!snd_soc_dpcm_be_can_update(fe, be, stream))
index 3c0d7cb..2ac5d9d 100644 (file)
@@ -65,7 +65,7 @@ static struct hdac_ext_stream *cl_stream_prepare(struct snd_sof_dev *sdev, unsig
        ret = snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV_SG, &pci->dev, size, dmab);
        if (ret < 0) {
                dev_err(sdev->dev, "error: memory alloc failed: %d\n", ret);
-               goto error;
+               goto out_put;
        }
 
        hstream->period_bytes = 0;/* initialize period_bytes */
@@ -76,22 +76,23 @@ static struct hdac_ext_stream *cl_stream_prepare(struct snd_sof_dev *sdev, unsig
                ret = hda_dsp_iccmax_stream_hw_params(sdev, hext_stream, dmab, NULL);
                if (ret < 0) {
                        dev_err(sdev->dev, "error: iccmax stream prepare failed: %d\n", ret);
-                       goto error;
+                       goto out_free;
                }
        } else {
                ret = hda_dsp_stream_hw_params(sdev, hext_stream, dmab, NULL);
                if (ret < 0) {
                        dev_err(sdev->dev, "error: hdac prepare failed: %d\n", ret);
-                       goto error;
+                       goto out_free;
                }
                hda_dsp_stream_spib_config(sdev, hext_stream, HDA_DSP_SPIB_ENABLE, size);
        }
 
        return hext_stream;
 
-error:
-       hda_dsp_stream_put(sdev, direction, hstream->stream_tag);
+out_free:
        snd_dma_free_pages(dmab);
+out_put:
+       hda_dsp_stream_put(sdev, direction, hstream->stream_tag);
        return ERR_PTR(ret);
 }
 
index 019f8d6..9c97c80 100644 (file)
@@ -916,6 +916,7 @@ int hda_dsp_probe(struct snd_sof_dev *sdev)
                dev_dbg(sdev->dev, "DMA mask is 32 bit\n");
                dma_set_mask_and_coherent(&pci->dev, DMA_BIT_MASK(32));
        }
+       dma_set_max_seg_size(&pci->dev, UINT_MAX);
 
        /* init streams */
        ret = hda_dsp_stream_init(sdev);
index 90d118c..3332fe3 100644 (file)
@@ -136,11 +136,7 @@ struct sound_unit
  * All these clutters are scheduled to be removed along with
  * sound-slot/service-* module aliases.
  */
-#ifdef CONFIG_SOUND_OSS_CORE_PRECLAIM
-static int preclaim_oss = 1;
-#else
-static int preclaim_oss = 0;
-#endif
+static int preclaim_oss = IS_ENABLED(CONFIG_SOUND_OSS_CORE_PRECLAIM);
 
 module_param(preclaim_oss, int, 0444);
 
@@ -581,20 +577,20 @@ static int soundcore_open(struct inode *inode, struct file *file)
                        new_fops = fops_get(s->unit_fops);
        }
        spin_unlock(&sound_loader_lock);
-       if (new_fops) {
-               /*
-                * We rely upon the fact that we can't be unloaded while the
-                * subdriver is there.
-                */
-               int err = 0;
-               replace_fops(file, new_fops);
 
-               if (file->f_op->open)
-                       err = file->f_op->open(inode,file);
+       if (!new_fops)
+               return -ENODEV;
 
-               return err;
-       }
-       return -ENODEV;
+       /*
+        * We rely upon the fact that we can't be unloaded while the
+        * subdriver is there.
+        */
+       replace_fops(file, new_fops);
+
+       if (!file->f_op->open)
+               return -ENODEV;
+
+       return file->f_op->open(inode, file);
 }
 
 MODULE_ALIAS_CHARDEV_MAJOR(SOUND_MAJOR);
index 76c0e37..8a2da6b 100644 (file)
@@ -218,7 +218,9 @@ static int snd_at73c213_pcm_open(struct snd_pcm_substream *substream)
        runtime->hw = snd_at73c213_playback_hw;
        chip->substream = substream;
 
-       clk_enable(chip->ssc->clk);
+       err = clk_enable(chip->ssc->clk);
+       if (err)
+               return err;
 
        return 0;
 }
@@ -776,7 +778,9 @@ static int snd_at73c213_chip_init(struct snd_at73c213 *chip)
                goto out;
 
        /* Enable DAC master clock. */
-       clk_enable(chip->board->dac_clk);
+       retval = clk_enable(chip->board->dac_clk);
+       if (retval)
+               goto out;
 
        /* Initialize at73c213 on SPI bus. */
        retval = snd_at73c213_write_reg(chip, DAC_RST, 0x04);
@@ -889,7 +893,9 @@ static int snd_at73c213_dev_init(struct snd_card *card,
        chip->card = card;
        chip->irq = -1;
 
-       clk_enable(chip->ssc->clk);
+       retval = clk_enable(chip->ssc->clk);
+       if (retval)
+               return retval;
 
        retval = request_irq(irq, snd_at73c213_interrupt, 0, "at73c213", chip);
        if (retval) {
@@ -1008,7 +1014,9 @@ static int snd_at73c213_remove(struct spi_device *spi)
        int retval;
 
        /* Stop playback. */
-       clk_enable(chip->ssc->clk);
+       retval = clk_enable(chip->ssc->clk);
+       if (retval)
+               goto out;
        ssc_writel(chip->ssc->regs, CR, SSC_BIT(CR_TXDIS));
        clk_disable(chip->ssc->clk);
 
@@ -1088,9 +1096,16 @@ static int snd_at73c213_resume(struct device *dev)
 {
        struct snd_card *card = dev_get_drvdata(dev);
        struct snd_at73c213 *chip = card->private_data;
+       int retval;
 
-       clk_enable(chip->board->dac_clk);
-       clk_enable(chip->ssc->clk);
+       retval = clk_enable(chip->board->dac_clk);
+       if (retval)
+               return retval;
+       retval = clk_enable(chip->ssc->clk);
+       if (retval) {
+               clk_disable(chip->board->dac_clk);
+               return retval;
+       }
        ssc_writel(chip->ssc->regs, CR, SSC_BIT(CR_TXEN));
 
        return 0;
index 70319c8..2d444ec 100644 (file)
@@ -47,13 +47,13 @@ struct snd_usb_implicit_fb_match {
 static const struct snd_usb_implicit_fb_match playback_implicit_fb_quirks[] = {
        /* Generic matching */
        IMPLICIT_FB_GENERIC_DEV(0x0499, 0x1509), /* Steinberg UR22 */
-       IMPLICIT_FB_GENERIC_DEV(0x0763, 0x2080), /* M-Audio FastTrack Ultra */
-       IMPLICIT_FB_GENERIC_DEV(0x0763, 0x2081), /* M-Audio FastTrack Ultra */
        IMPLICIT_FB_GENERIC_DEV(0x0763, 0x2030), /* M-Audio Fast Track C400 */
        IMPLICIT_FB_GENERIC_DEV(0x0763, 0x2031), /* M-Audio Fast Track C600 */
 
        /* Fixed EP */
        /* FIXME: check the availability of generic matching */
+       IMPLICIT_FB_FIXED_DEV(0x0763, 0x2080, 0x81, 2), /* M-Audio FastTrack Ultra */
+       IMPLICIT_FB_FIXED_DEV(0x0763, 0x2081, 0x81, 2), /* M-Audio FastTrack Ultra */
        IMPLICIT_FB_FIXED_DEV(0x2466, 0x8010, 0x81, 2), /* Fractal Audio Axe-Fx III */
        IMPLICIT_FB_FIXED_DEV(0x31e9, 0x0001, 0x81, 2), /* Solid State Logic SSL2 */
        IMPLICIT_FB_FIXED_DEV(0x31e9, 0x0002, 0x81, 2), /* Solid State Logic SSL2+ */
index e8f3f8d..a564195 100644 (file)
@@ -1527,6 +1527,10 @@ error:
                usb_audio_err(chip,
                        "cannot get connectors status: req = %#x, wValue = %#x, wIndex = %#x, type = %d\n",
                        UAC_GET_CUR, validx, idx, cval->val_type);
+
+               if (val)
+                       *val = 0;
+
                return filter_error(cval, ret);
        }
 
@@ -3674,17 +3678,14 @@ static int restore_mixer_value(struct usb_mixer_elem_list *list)
                                err = snd_usb_set_cur_mix_value(cval, c + 1, idx,
                                                        cval->cache_val[idx]);
                                if (err < 0)
-                                       return err;
+                                       break;
                        }
                        idx++;
                }
        } else {
                /* master */
-               if (cval->cached) {
-                       err = snd_usb_set_cur_mix_value(cval, 0, 0, *cval->cache_val);
-                       if (err < 0)
-                               return err;
-               }
+               if (cval->cached)
+                       snd_usb_set_cur_mix_value(cval, 0, 0, *cval->cache_val);
        }
 
        return 0;
index 96991dd..64f5544 100644 (file)
@@ -542,6 +542,16 @@ static const struct usbmix_ctl_map usbmix_ctl_maps[] = {
                .id = USB_ID(0x05a7, 0x40fa),
                .map = bose_soundlink_map,
        },
+       {
+               /* Corsair Virtuoso SE Latest (wired mode) */
+               .id = USB_ID(0x1b1c, 0x0a3f),
+               .map = corsair_virtuoso_map,
+       },
+       {
+               /* Corsair Virtuoso SE Latest (wireless mode) */
+               .id = USB_ID(0x1b1c, 0x0a40),
+               .map = corsair_virtuoso_map,
+       },
        {
                /* Corsair Virtuoso SE (wired mode) */
                .id = USB_ID(0x1b1c, 0x0a3d),
index e447ddd..d35cf54 100644 (file)
@@ -3360,9 +3360,10 @@ void snd_usb_mixer_fu_apply_quirk(struct usb_mixer_interface *mixer,
                if (unitid == 7 && cval->control == UAC_FU_VOLUME)
                        snd_dragonfly_quirk_db_scale(mixer, cval, kctl);
                break;
-       /* lowest playback value is muted on C-Media devices */
-       case USB_ID(0x0d8c, 0x000c):
-       case USB_ID(0x0d8c, 0x0014):
+       /* lowest playback value is muted on some devices */
+       case USB_ID(0x0d8c, 0x000c): /* C-Media */
+       case USB_ID(0x0d8c, 0x0014): /* C-Media */
+       case USB_ID(0x19f7, 0x0003): /* RODE NT-USB */
                if (strstr(kctl->id.name, "Playback"))
                        cval->min_mute = 1;
                break;
index 0255089..fac4bbc 100644 (file)
@@ -221,7 +221,7 @@ static int snd_s1810c_init_mixer_maps(struct snd_usb_audio *chip)
        e = 0xbc;
        for (n = 0; n < 2; n++) {
                off = n * 18;
-               for (b = off, c = 0; b < 18 + off; b++) {
+               for (b = off; b < 18 + off; b++) {
                        /* This channel to all outputs ? */
                        for (c = 0; c <= 8; c++) {
                                snd_s1810c_send_ctl_packet(dev, a, b, c, 0, e);
index 53ebabf..69a2cd4 100644 (file)
@@ -6,7 +6,7 @@
  *   - 6i6/18i8/18i20 Gen 2
  *   - Solo/2i2/4i4/8i6/18i8/18i20 Gen 3
  *
- *   Copyright (c) 2018-2021 by Geoffrey D. Bennett <g at b4.vu>
+ *   Copyright (c) 2018-2022 by Geoffrey D. Bennett <g at b4.vu>
  *   Copyright (c) 2020-2021 by Vladimir Sadovnikov <sadko4u@gmail.com>
  *
  *   Based on the Scarlett (Gen 1) Driver for ALSA:
@@ -60,6 +60,7 @@
  *  - phantom power, direct monitor, speaker switching, and talkback
  *    controls
  *  - disable/enable MSD mode
+ *  - disable/enable standalone mode
  *
  * <ditaa>
  *    /--------------\    18chn            20chn     /--------------\
@@ -195,6 +196,16 @@ static const u16 scarlett2_mixer_values[SCARLETT2_MIXER_VALUE_COUNT] = {
 /* Maximum number of meters (sum of output port counts) */
 #define SCARLETT2_MAX_METERS 65
 
+/* There are three different sets of configuration parameters across
+ * the devices
+ */
+enum {
+       SCARLETT2_CONFIG_SET_NO_MIXER = 0,
+       SCARLETT2_CONFIG_SET_GEN_2 = 1,
+       SCARLETT2_CONFIG_SET_GEN_3 = 2,
+       SCARLETT2_CONFIG_SET_COUNT = 3
+};
+
 /* Hardware port types:
  * - None (no input to mux)
  * - Analogue I/O
@@ -308,10 +319,8 @@ struct scarlett2_device_info {
         */
        u8 has_msd_mode;
 
-       /* Gen 3 devices without a mixer have a different
-        * configuration set
-        */
-       u8 has_mixer;
+       /* which set of configuration parameters the device uses */
+       u8 config_set;
 
        /* line out hw volume is sw controlled */
        u8 line_out_hw_vol;
@@ -403,6 +412,7 @@ struct scarlett2_data {
        u8 talkback_switch;
        u8 talkback_map[SCARLETT2_OUTPUT_MIX_MAX];
        u8 msd_switch;
+       u8 standalone_switch;
        struct snd_kcontrol *sync_ctl;
        struct snd_kcontrol *master_vol_ctl;
        struct snd_kcontrol *vol_ctls[SCARLETT2_ANALOGUE_MAX];
@@ -426,7 +436,7 @@ struct scarlett2_data {
 static const struct scarlett2_device_info s6i6_gen2_info = {
        .usb_id = USB_ID(0x1235, 0x8203),
 
-       .has_mixer = 1,
+       .config_set = SCARLETT2_CONFIG_SET_GEN_2,
        .level_input_count = 2,
        .pad_input_count = 2,
 
@@ -472,7 +482,7 @@ static const struct scarlett2_device_info s6i6_gen2_info = {
 static const struct scarlett2_device_info s18i8_gen2_info = {
        .usb_id = USB_ID(0x1235, 0x8204),
 
-       .has_mixer = 1,
+       .config_set = SCARLETT2_CONFIG_SET_GEN_2,
        .level_input_count = 2,
        .pad_input_count = 4,
 
@@ -521,7 +531,7 @@ static const struct scarlett2_device_info s18i8_gen2_info = {
 static const struct scarlett2_device_info s18i20_gen2_info = {
        .usb_id = USB_ID(0x1235, 0x8201),
 
-       .has_mixer = 1,
+       .config_set = SCARLETT2_CONFIG_SET_GEN_2,
        .line_out_hw_vol = 1,
 
        .line_out_descrs = {
@@ -576,6 +586,7 @@ static const struct scarlett2_device_info solo_gen3_info = {
        .usb_id = USB_ID(0x1235, 0x8211),
 
        .has_msd_mode = 1,
+       .config_set = SCARLETT2_CONFIG_SET_NO_MIXER,
        .level_input_count = 1,
        .level_input_first = 1,
        .air_input_count = 1,
@@ -588,6 +599,7 @@ static const struct scarlett2_device_info s2i2_gen3_info = {
        .usb_id = USB_ID(0x1235, 0x8210),
 
        .has_msd_mode = 1,
+       .config_set = SCARLETT2_CONFIG_SET_NO_MIXER,
        .level_input_count = 2,
        .air_input_count = 2,
        .phantom_count = 1,
@@ -599,7 +611,7 @@ static const struct scarlett2_device_info s4i4_gen3_info = {
        .usb_id = USB_ID(0x1235, 0x8212),
 
        .has_msd_mode = 1,
-       .has_mixer = 1,
+       .config_set = SCARLETT2_CONFIG_SET_GEN_3,
        .level_input_count = 2,
        .pad_input_count = 2,
        .air_input_count = 2,
@@ -645,7 +657,7 @@ static const struct scarlett2_device_info s8i6_gen3_info = {
        .usb_id = USB_ID(0x1235, 0x8213),
 
        .has_msd_mode = 1,
-       .has_mixer = 1,
+       .config_set = SCARLETT2_CONFIG_SET_GEN_3,
        .level_input_count = 2,
        .pad_input_count = 2,
        .air_input_count = 2,
@@ -698,7 +710,7 @@ static const struct scarlett2_device_info s18i8_gen3_info = {
        .usb_id = USB_ID(0x1235, 0x8214),
 
        .has_msd_mode = 1,
-       .has_mixer = 1,
+       .config_set = SCARLETT2_CONFIG_SET_GEN_3,
        .line_out_hw_vol = 1,
        .has_speaker_switching = 1,
        .level_input_count = 2,
@@ -768,7 +780,7 @@ static const struct scarlett2_device_info s18i20_gen3_info = {
        .usb_id = USB_ID(0x1235, 0x8215),
 
        .has_msd_mode = 1,
-       .has_mixer = 1,
+       .config_set = SCARLETT2_CONFIG_SET_GEN_3,
        .line_out_hw_vol = 1,
        .has_speaker_switching = 1,
        .has_talkback = 1,
@@ -926,13 +938,14 @@ enum {
        SCARLETT2_CONFIG_PAD_SWITCH = 5,
        SCARLETT2_CONFIG_MSD_SWITCH = 6,
        SCARLETT2_CONFIG_AIR_SWITCH = 7,
-       SCARLETT2_CONFIG_PHANTOM_SWITCH = 8,
-       SCARLETT2_CONFIG_PHANTOM_PERSISTENCE = 9,
-       SCARLETT2_CONFIG_DIRECT_MONITOR = 10,
-       SCARLETT2_CONFIG_MONITOR_OTHER_SWITCH = 11,
-       SCARLETT2_CONFIG_MONITOR_OTHER_ENABLE = 12,
-       SCARLETT2_CONFIG_TALKBACK_MAP = 13,
-       SCARLETT2_CONFIG_COUNT = 14
+       SCARLETT2_CONFIG_STANDALONE_SWITCH = 8,
+       SCARLETT2_CONFIG_PHANTOM_SWITCH = 9,
+       SCARLETT2_CONFIG_PHANTOM_PERSISTENCE = 10,
+       SCARLETT2_CONFIG_DIRECT_MONITOR = 11,
+       SCARLETT2_CONFIG_MONITOR_OTHER_SWITCH = 12,
+       SCARLETT2_CONFIG_MONITOR_OTHER_ENABLE = 13,
+       SCARLETT2_CONFIG_TALKBACK_MAP = 14,
+       SCARLETT2_CONFIG_COUNT = 15
 };
 
 /* Location, size, and activation command number for the configuration
@@ -944,13 +957,11 @@ struct scarlett2_config {
        u8 activate;
 };
 
-/* scarlett2_config_items[0] is for devices without a mixer
- * scarlett2_config_items[1] is for devices with a mixer
- */
 static const struct scarlett2_config
-       scarlett2_config_items[2][SCARLETT2_CONFIG_COUNT] =
+       scarlett2_config_items[SCARLETT2_CONFIG_SET_COUNT]
+                             [SCARLETT2_CONFIG_COUNT] =
 
-/* Devices without a mixer (Solo and 2i2 Gen 3) */
+/* Devices without a mixer (Gen 3 Solo and 2i2) */
 { {
        [SCARLETT2_CONFIG_MSD_SWITCH] = {
                .offset = 0x04, .size = 8, .activate = 6 },
@@ -970,7 +981,30 @@ static const struct scarlett2_config
        [SCARLETT2_CONFIG_AIR_SWITCH] = {
                .offset = 0x09, .size = 1, .activate = 8 },
 
-/* Devices with a mixer (Gen 2 and all other Gen 3) */
+/* Gen 2 devices: 6i6, 18i8, 18i20 */
+}, {
+       [SCARLETT2_CONFIG_DIM_MUTE] = {
+               .offset = 0x31, .size = 8, .activate = 2 },
+
+       [SCARLETT2_CONFIG_LINE_OUT_VOLUME] = {
+               .offset = 0x34, .size = 16, .activate = 1 },
+
+       [SCARLETT2_CONFIG_MUTE_SWITCH] = {
+               .offset = 0x5c, .size = 8, .activate = 1 },
+
+       [SCARLETT2_CONFIG_SW_HW_SWITCH] = {
+               .offset = 0x66, .size = 8, .activate = 3 },
+
+       [SCARLETT2_CONFIG_LEVEL_SWITCH] = {
+               .offset = 0x7c, .size = 8, .activate = 7 },
+
+       [SCARLETT2_CONFIG_PAD_SWITCH] = {
+               .offset = 0x84, .size = 8, .activate = 8 },
+
+       [SCARLETT2_CONFIG_STANDALONE_SWITCH] = {
+               .offset = 0x8d, .size = 8, .activate = 6 },
+
+/* Gen 3 devices: 4i4, 8i6, 18i8, 18i20 */
 }, {
        [SCARLETT2_CONFIG_DIM_MUTE] = {
                .offset = 0x31, .size = 8, .activate = 2 },
@@ -993,6 +1027,9 @@ static const struct scarlett2_config
        [SCARLETT2_CONFIG_AIR_SWITCH] = {
                .offset = 0x8c, .size = 8, .activate = 8 },
 
+       [SCARLETT2_CONFIG_STANDALONE_SWITCH] = {
+               .offset = 0x95, .size = 8, .activate = 6 },
+
        [SCARLETT2_CONFIG_PHANTOM_SWITCH] = {
                .offset = 0x9c, .size = 1, .activate = 8 },
 
@@ -1061,9 +1098,9 @@ static int scarlett2_usb(
 {
        struct scarlett2_data *private = mixer->private_data;
        struct usb_device *dev = mixer->chip->dev;
-       u16 req_buf_size = sizeof(struct scarlett2_usb_packet) + req_size;
-       u16 resp_buf_size = sizeof(struct scarlett2_usb_packet) + resp_size;
        struct scarlett2_usb_packet *req, *resp = NULL;
+       size_t req_buf_size = struct_size(req, data, req_size);
+       size_t resp_buf_size = struct_size(resp, data, resp_size);
        int err;
 
        req = kmalloc(req_buf_size, GFP_KERNEL);
@@ -1111,7 +1148,7 @@ static int scarlett2_usb(
                usb_audio_err(
                        mixer->chip,
                        "Scarlett Gen 2/3 USB response result cmd %x was %d "
-                       "expected %d\n",
+                       "expected %zu\n",
                        cmd, err, resp_buf_size);
                err = -EINVAL;
                goto unlock;
@@ -1175,7 +1212,7 @@ static int scarlett2_usb_get_config(
        struct scarlett2_data *private = mixer->private_data;
        const struct scarlett2_device_info *info = private->info;
        const struct scarlett2_config *config_item =
-               &scarlett2_config_items[info->has_mixer][config_item_num];
+               &scarlett2_config_items[info->config_set][config_item_num];
        int size, err, i;
        u8 *buf_8;
        u8 value;
@@ -1235,7 +1272,7 @@ static int scarlett2_usb_set_config(
        struct scarlett2_data *private = mixer->private_data;
        const struct scarlett2_device_info *info = private->info;
        const struct scarlett2_config *config_item =
-              &scarlett2_config_items[info->has_mixer][config_item_num];
+              &scarlett2_config_items[info->config_set][config_item_num];
        struct {
                __le32 offset;
                __le32 bytes;
@@ -1692,7 +1729,7 @@ static int scarlett2_add_sync_ctl(struct usb_mixer_interface *mixer)
        struct scarlett2_data *private = mixer->private_data;
 
        /* devices without a mixer also don't support reporting sync status */
-       if (!private->info->has_mixer)
+       if (private->info->config_set == SCARLETT2_CONFIG_SET_NO_MIXER)
                return 0;
 
        return scarlett2_add_new_ctl(mixer, &scarlett2_sync_ctl,
@@ -3399,7 +3436,7 @@ static int scarlett2_add_meter_ctl(struct usb_mixer_interface *mixer)
        struct scarlett2_data *private = mixer->private_data;
 
        /* devices without a mixer also don't support reporting levels */
-       if (!private->info->has_mixer)
+       if (private->info->config_set == SCARLETT2_CONFIG_SET_NO_MIXER)
                return 0;
 
        return scarlett2_add_new_ctl(mixer, &scarlett2_meter_ctl,
@@ -3474,6 +3511,69 @@ static int scarlett2_add_msd_ctl(struct usb_mixer_interface *mixer)
                                     0, 1, "MSD Mode Switch", NULL);
 }
 
+/*** Standalone Control ***/
+
+static int scarlett2_standalone_ctl_get(struct snd_kcontrol *kctl,
+                                       struct snd_ctl_elem_value *ucontrol)
+{
+       struct usb_mixer_elem_info *elem = kctl->private_data;
+       struct scarlett2_data *private = elem->head.mixer->private_data;
+
+       ucontrol->value.integer.value[0] = private->standalone_switch;
+       return 0;
+}
+
+static int scarlett2_standalone_ctl_put(struct snd_kcontrol *kctl,
+                                       struct snd_ctl_elem_value *ucontrol)
+{
+       struct usb_mixer_elem_info *elem = kctl->private_data;
+       struct usb_mixer_interface *mixer = elem->head.mixer;
+       struct scarlett2_data *private = mixer->private_data;
+
+       int oval, val, err = 0;
+
+       mutex_lock(&private->data_mutex);
+
+       oval = private->standalone_switch;
+       val = !!ucontrol->value.integer.value[0];
+
+       if (oval == val)
+               goto unlock;
+
+       private->standalone_switch = val;
+
+       /* Send switch change to the device */
+       err = scarlett2_usb_set_config(mixer,
+                                      SCARLETT2_CONFIG_STANDALONE_SWITCH,
+                                      0, val);
+       if (err == 0)
+               err = 1;
+
+unlock:
+       mutex_unlock(&private->data_mutex);
+       return err;
+}
+
+static const struct snd_kcontrol_new scarlett2_standalone_ctl = {
+       .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+       .name = "",
+       .info = snd_ctl_boolean_mono_info,
+       .get  = scarlett2_standalone_ctl_get,
+       .put  = scarlett2_standalone_ctl_put,
+};
+
+static int scarlett2_add_standalone_ctl(struct usb_mixer_interface *mixer)
+{
+       struct scarlett2_data *private = mixer->private_data;
+
+       if (private->info->config_set == SCARLETT2_CONFIG_SET_NO_MIXER)
+               return 0;
+
+       /* Add standalone control */
+       return scarlett2_add_new_ctl(mixer, &scarlett2_standalone_ctl,
+                                    0, 1, "Standalone Switch", NULL);
+}
+
 /*** Cleanup/Suspend Callbacks ***/
 
 static void scarlett2_private_free(struct usb_mixer_interface *mixer)
@@ -3632,9 +3732,15 @@ static int scarlett2_read_configs(struct usb_mixer_interface *mixer)
                return err;
 
        /* the rest of the configuration is for devices with a mixer */
-       if (!info->has_mixer)
+       if (info->config_set == SCARLETT2_CONFIG_SET_NO_MIXER)
                return 0;
 
+       err = scarlett2_usb_get_config(
+               mixer, SCARLETT2_CONFIG_STANDALONE_SWITCH,
+               1, &private->standalone_switch);
+       if (err < 0)
+               return err;
+
        err = scarlett2_update_sync(mixer);
        if (err < 0)
                return err;
@@ -3957,6 +4063,11 @@ static int snd_scarlett_gen2_controls_create(struct usb_mixer_interface *mixer)
        if (err < 0)
                return err;
 
+       /* Create the standalone control */
+       err = scarlett2_add_standalone_ctl(mixer);
+       if (err < 0)
+               return err;
+
        /* Set up the interrupt polling */
        err = scarlett2_init_notify(mixer);
        if (err < 0)
index b1522e4..0ea3956 100644 (file)
@@ -84,7 +84,7 @@
  * combination.
  */
 {
-       USB_DEVICE(0x041e, 0x4095),
+       USB_AUDIO_DEVICE(0x041e, 0x4095),
        .driver_info = (unsigned long) &(const struct snd_usb_audio_quirk) {
                .ifnum = QUIRK_ANY_INTERFACE,
                .type = QUIRK_COMPOSITE,
index 1c94eaf..b006346 100644 (file)
@@ -1253,18 +1253,6 @@ static snd_pcm_uframes_t had_pcm_pointer(struct snd_pcm_substream *substream)
        return len;
 }
 
-/*
- * ALSA PCM mmap callback
- */
-static int had_pcm_mmap(struct snd_pcm_substream *substream,
-                       struct vm_area_struct *vma)
-{
-       vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
-       return remap_pfn_range(vma, vma->vm_start,
-                       substream->dma_buffer.addr >> PAGE_SHIFT,
-                       vma->vm_end - vma->vm_start, vma->vm_page_prot);
-}
-
 /*
  * ALSA PCM ops
  */
@@ -1276,7 +1264,6 @@ static const struct snd_pcm_ops had_pcm_ops = {
        .trigger =      had_pcm_trigger,
        .sync_stop =    had_pcm_sync_stop,
        .pointer =      had_pcm_pointer,
-       .mmap =         had_pcm_mmap,
 };
 
 /* process mode change of the running stream; called in mutex */
index 17f158d..eb22135 100644 (file)
@@ -3,7 +3,7 @@
 // kselftest for the ALSA mixer API
 //
 // Original author: Mark Brown <broonie@kernel.org>
-// Copyright (c) 2021 Arm Limited
+// Copyright (c) 2021-2 Arm Limited
 
 // This test will iterate over all cards detected in the system, exercising
 // every mixer control it can find.  This may conflict with other system
@@ -13,6 +13,7 @@
 #include <stdio.h>
 #include <stdlib.h>
 #include <stdbool.h>
+#include <limits.h>
 #include <string.h>
 #include <getopt.h>
 #include <stdarg.h>
 
 #include "../kselftest.h"
 
-#define TESTS_PER_CONTROL 3
+#define TESTS_PER_CONTROL 6
 
 struct card_data {
        snd_ctl_t *handle;
        int card;
+       struct pollfd pollfd;
        int num_ctls;
        snd_ctl_elem_list_t *ctls;
        struct card_data *next;
@@ -42,6 +44,8 @@ struct ctl_data {
        snd_ctl_elem_info_t *info;
        snd_ctl_elem_value_t *def_val;
        int elem;
+       int event_missing;
+       int event_spurious;
        struct card_data *card;
        struct ctl_data *next;
 };
@@ -67,7 +71,8 @@ struct ctl_data *ctl_list = NULL;
 #endif
 
 #ifndef LIB_HAS_LOAD_STRING
-int snd_config_load_string(snd_config_t **config, const char *s, size_t size)
+static int snd_config_load_string(snd_config_t **config, const char *s,
+                                 size_t size)
 {
        snd_input_t *input;
        snd_config_t *dst;
@@ -95,7 +100,7 @@ int snd_config_load_string(snd_config_t **config, const char *s, size_t size)
 }
 #endif
 
-void find_controls(void)
+static void find_controls(void)
 {
        char name[32];
        int card, ctl, err;
@@ -148,6 +153,7 @@ void find_controls(void)
                        if (!ctl_data)
                                ksft_exit_fail_msg("Out of memory\n");
 
+                       memset(ctl_data, 0, sizeof(*ctl_data));
                        ctl_data->card = card_data;
                        ctl_data->elem = ctl;
                        ctl_data->name = snd_ctl_elem_list_get_name(card_data->ctls,
@@ -183,6 +189,26 @@ void find_controls(void)
                        ctl_list = ctl_data;
                }
 
+               /* Set up for events */
+               err = snd_ctl_subscribe_events(card_data->handle, true);
+               if (err < 0) {
+                       ksft_exit_fail_msg("snd_ctl_subscribe_events() failed for card %d: %d\n",
+                                          card, err);
+               }
+
+               err = snd_ctl_poll_descriptors_count(card_data->handle);
+               if (err != 1) {
+                       ksft_exit_fail_msg("Unexpected descriptor count %d for card %d\n",
+                                          err, card);
+               }
+
+               err = snd_ctl_poll_descriptors(card_data->handle,
+                                              &card_data->pollfd, 1);
+               if (err != 1) {
+                       ksft_exit_fail_msg("snd_ctl_poll_descriptors() failed for %d\n",
+                                      card, err);
+               }
+
        next_card:
                if (snd_card_next(&card) < 0) {
                        ksft_print_msg("snd_card_next");
@@ -193,8 +219,82 @@ void find_controls(void)
        snd_config_delete(config);
 }
 
-bool ctl_value_index_valid(struct ctl_data *ctl, snd_ctl_elem_value_t *val,
-                          int index)
+/*
+ * Block for up to timeout ms for an event, returns a negative value
+ * on error, 0 for no event and 1 for an event.
+ */
+static int wait_for_event(struct ctl_data *ctl, int timeout)
+{
+       unsigned short revents;
+       snd_ctl_event_t *event;
+       int count, err;
+       unsigned int mask = 0;
+       unsigned int ev_id;
+
+       snd_ctl_event_alloca(&event);
+
+       do {
+               err = poll(&(ctl->card->pollfd), 1, timeout);
+               if (err < 0) {
+                       ksft_print_msg("poll() failed for %s: %s (%d)\n",
+                                      ctl->name, strerror(errno), errno);
+                       return -1;
+               }
+               /* Timeout */
+               if (err == 0)
+                       return 0;
+
+               err = snd_ctl_poll_descriptors_revents(ctl->card->handle,
+                                                      &(ctl->card->pollfd),
+                                                      1, &revents);
+               if (err < 0) {
+                       ksft_print_msg("snd_ctl_poll_descriptors_revents() failed for %s: %d\n",
+                                      ctl->name, err);
+                       return err;
+               }
+               if (revents & POLLERR) {
+                       ksft_print_msg("snd_ctl_poll_descriptors_revents() reported POLLERR for %s\n",
+                                      ctl->name);
+                       return -1;
+               }
+               /* No read events */
+               if (!(revents & POLLIN)) {
+                       ksft_print_msg("No POLLIN\n");
+                       continue;
+               }
+
+               err = snd_ctl_read(ctl->card->handle, event);
+               if (err < 0) {
+                       ksft_print_msg("snd_ctl_read() failed for %s: %d\n",
+                              ctl->name, err);
+                       return err;
+               }
+
+               if (snd_ctl_event_get_type(event) != SND_CTL_EVENT_ELEM)
+                       continue;
+
+               /* The ID returned from the event is 1 less than numid */
+               mask = snd_ctl_event_elem_get_mask(event);
+               ev_id = snd_ctl_event_elem_get_numid(event);
+               if (ev_id != snd_ctl_elem_info_get_numid(ctl->info)) {
+                       ksft_print_msg("Event for unexpected ctl %s\n",
+                                      snd_ctl_event_elem_get_name(event));
+                       continue;
+               }
+
+               if ((mask & SND_CTL_EVENT_MASK_REMOVE) == SND_CTL_EVENT_MASK_REMOVE) {
+                       ksft_print_msg("Removal event for %s\n",
+                                      ctl->name);
+                       return -1;
+               }
+       } while ((mask & SND_CTL_EVENT_MASK_VALUE) != SND_CTL_EVENT_MASK_VALUE);
+
+       return 1;
+}
+
+static bool ctl_value_index_valid(struct ctl_data *ctl,
+                                 snd_ctl_elem_value_t *val,
+                                 int index)
 {
        long int_val;
        long long int64_val;
@@ -305,7 +405,7 @@ bool ctl_value_index_valid(struct ctl_data *ctl, snd_ctl_elem_value_t *val,
  * Check that the provided value meets the constraints for the
  * provided control.
  */
-bool ctl_value_valid(struct ctl_data *ctl, snd_ctl_elem_value_t *val)
+static bool ctl_value_valid(struct ctl_data *ctl, snd_ctl_elem_value_t *val)
 {
        int i;
        bool valid = true;
@@ -321,7 +421,7 @@ bool ctl_value_valid(struct ctl_data *ctl, snd_ctl_elem_value_t *val)
  * Check that we can read the default value and it is valid. Write
  * tests use the read value to restore the default.
  */
-void test_ctl_get_value(struct ctl_data *ctl)
+static void test_ctl_get_value(struct ctl_data *ctl)
 {
        int err;
 
@@ -356,9 +456,9 @@ out:
                         ctl->card->card, ctl->elem);
 }
 
-bool show_mismatch(struct ctl_data *ctl, int index,
-                  snd_ctl_elem_value_t *read_val,
-                  snd_ctl_elem_value_t *expected_val)
+static bool show_mismatch(struct ctl_data *ctl, int index,
+                         snd_ctl_elem_value_t *read_val,
+                         snd_ctl_elem_value_t *expected_val)
 {
        long long expected_int, read_int;
 
@@ -421,13 +521,14 @@ bool show_mismatch(struct ctl_data *ctl, int index,
  * the write to fail, for verifying that invalid writes don't corrupt
  * anything.
  */
-int write_and_verify(struct ctl_data *ctl,
-                    snd_ctl_elem_value_t *write_val,
-                    snd_ctl_elem_value_t *expected_val)
+static int write_and_verify(struct ctl_data *ctl,
+                           snd_ctl_elem_value_t *write_val,
+                           snd_ctl_elem_value_t *expected_val)
 {
        int err, i;
        bool error_expected, mismatch_shown;
-       snd_ctl_elem_value_t *read_val, *w_val;
+       snd_ctl_elem_value_t *initial_val, *read_val, *w_val;
+       snd_ctl_elem_value_alloca(&initial_val);
        snd_ctl_elem_value_alloca(&read_val);
        snd_ctl_elem_value_alloca(&w_val);
 
@@ -445,6 +546,18 @@ int write_and_verify(struct ctl_data *ctl,
                snd_ctl_elem_value_copy(expected_val, write_val);
        }
 
+       /* Store the value before we write */
+       if (snd_ctl_elem_info_is_readable(ctl->info)) {
+               snd_ctl_elem_value_set_id(initial_val, ctl->id);
+
+               err = snd_ctl_elem_read(ctl->card->handle, initial_val);
+               if (err < 0) {
+                       ksft_print_msg("snd_ctl_elem_read() failed: %s\n",
+                                      snd_strerror(err));
+                       return err;
+               }
+       }
+
        /*
         * Do the write, if we have an expected value ignore the error
         * and carry on to validate the expected value.
@@ -469,6 +582,30 @@ int write_and_verify(struct ctl_data *ctl,
                return err;
        }
 
+       /*
+        * Check for an event if the value changed, or confirm that
+        * there was none if it didn't.  We rely on the kernel
+        * generating the notification before it returns from the
+        * write, this is currently true, should that ever change this
+        * will most likely break and need updating.
+        */
+       if (!snd_ctl_elem_info_is_volatile(ctl->info)) {
+               err = wait_for_event(ctl, 0);
+               if (snd_ctl_elem_value_compare(initial_val, read_val)) {
+                       if (err < 1) {
+                               ksft_print_msg("No event generated for %s\n",
+                                              ctl->name);
+                               ctl->event_missing++;
+                       }
+               } else {
+                       if (err != 0) {
+                               ksft_print_msg("Spurious event generated for %s\n",
+                                              ctl->name);
+                               ctl->event_spurious++;
+                       }
+               }
+       }
+
        /*
         * Use the libray to compare values, if there's a mismatch
         * carry on and try to provide a more useful diagnostic than
@@ -493,7 +630,7 @@ int write_and_verify(struct ctl_data *ctl,
  * Make sure we can write the default value back to the control, this
  * should validate that at least some write works.
  */
-void test_ctl_write_default(struct ctl_data *ctl)
+static void test_ctl_write_default(struct ctl_data *ctl)
 {
        int err;
 
@@ -526,7 +663,7 @@ void test_ctl_write_default(struct ctl_data *ctl)
                         ctl->card->card, ctl->elem);
 }
 
-bool test_ctl_write_valid_boolean(struct ctl_data *ctl)
+static bool test_ctl_write_valid_boolean(struct ctl_data *ctl)
 {
        int err, i, j;
        bool fail = false;
@@ -547,7 +684,7 @@ bool test_ctl_write_valid_boolean(struct ctl_data *ctl)
        return !fail;
 }
 
-bool test_ctl_write_valid_integer(struct ctl_data *ctl)
+static bool test_ctl_write_valid_integer(struct ctl_data *ctl)
 {
        int err;
        int i;
@@ -577,7 +714,7 @@ bool test_ctl_write_valid_integer(struct ctl_data *ctl)
        return !fail;
 }
 
-bool test_ctl_write_valid_integer64(struct ctl_data *ctl)
+static bool test_ctl_write_valid_integer64(struct ctl_data *ctl)
 {
        int err, i;
        long long j, step;
@@ -605,7 +742,7 @@ bool test_ctl_write_valid_integer64(struct ctl_data *ctl)
        return !fail;
 }
 
-bool test_ctl_write_valid_enumerated(struct ctl_data *ctl)
+static bool test_ctl_write_valid_enumerated(struct ctl_data *ctl)
 {
        int err, i, j;
        bool fail = false;
@@ -626,7 +763,7 @@ bool test_ctl_write_valid_enumerated(struct ctl_data *ctl)
        return !fail;
 }
 
-void test_ctl_write_valid(struct ctl_data *ctl)
+static void test_ctl_write_valid(struct ctl_data *ctl)
 {
        bool pass;
        int err;
@@ -679,6 +816,236 @@ void test_ctl_write_valid(struct ctl_data *ctl)
                         ctl->card->card, ctl->elem);
 }
 
+static bool test_ctl_write_invalid_value(struct ctl_data *ctl,
+                                        snd_ctl_elem_value_t *val)
+{
+       int err;
+       long val_read;
+
+       /* Ideally this will fail... */
+       err = snd_ctl_elem_write(ctl->card->handle, val);
+       if (err < 0)
+               return false;
+
+       /* ...but some devices will clamp to an in range value */
+       err = snd_ctl_elem_read(ctl->card->handle, val);
+       if (err < 0) {
+               ksft_print_msg("%s failed to read: %s\n",
+                              ctl->name, snd_strerror(err));
+               return true;
+       }
+
+       return !ctl_value_valid(ctl, val);
+}
+
+static bool test_ctl_write_invalid_boolean(struct ctl_data *ctl)
+{
+       int err, i;
+       long val_read;
+       bool fail = false;
+       snd_ctl_elem_value_t *val;
+       snd_ctl_elem_value_alloca(&val);
+
+       for (i = 0; i < snd_ctl_elem_info_get_count(ctl->info); i++) {
+               snd_ctl_elem_value_copy(val, ctl->def_val);
+               snd_ctl_elem_value_set_boolean(val, i, 2);
+
+               if (test_ctl_write_invalid_value(ctl, val))
+                       fail = true;
+       }
+
+       return !fail;
+}
+
+static bool test_ctl_write_invalid_integer(struct ctl_data *ctl)
+{
+       int i;
+       bool fail = false;
+       snd_ctl_elem_value_t *val;
+       snd_ctl_elem_value_alloca(&val);
+
+       for (i = 0; i < snd_ctl_elem_info_get_count(ctl->info); i++) {
+               if (snd_ctl_elem_info_get_min(ctl->info) != LONG_MIN) {
+                       /* Just under range */
+                       snd_ctl_elem_value_copy(val, ctl->def_val);
+                       snd_ctl_elem_value_set_integer(val, i,
+                              snd_ctl_elem_info_get_min(ctl->info) - 1);
+
+                       if (test_ctl_write_invalid_value(ctl, val))
+                               fail = true;
+
+                       /* Minimum representable value */
+                       snd_ctl_elem_value_copy(val, ctl->def_val);
+                       snd_ctl_elem_value_set_integer(val, i, LONG_MIN);
+
+                       if (test_ctl_write_invalid_value(ctl, val))
+                               fail = true;
+               }
+
+               if (snd_ctl_elem_info_get_max(ctl->info) != LONG_MAX) {
+                       /* Just over range */
+                       snd_ctl_elem_value_copy(val, ctl->def_val);
+                       snd_ctl_elem_value_set_integer(val, i,
+                              snd_ctl_elem_info_get_max(ctl->info) + 1);
+
+                       if (test_ctl_write_invalid_value(ctl, val))
+                               fail = true;
+
+                       /* Maximum representable value */
+                       snd_ctl_elem_value_copy(val, ctl->def_val);
+                       snd_ctl_elem_value_set_integer(val, i, LONG_MAX);
+
+                       if (test_ctl_write_invalid_value(ctl, val))
+                               fail = true;
+               }
+       }
+
+       return !fail;
+}
+
+static bool test_ctl_write_invalid_integer64(struct ctl_data *ctl)
+{
+       int i;
+       bool fail = false;
+       snd_ctl_elem_value_t *val;
+       snd_ctl_elem_value_alloca(&val);
+
+       for (i = 0; i < snd_ctl_elem_info_get_count(ctl->info); i++) {
+               if (snd_ctl_elem_info_get_min64(ctl->info) != LLONG_MIN) {
+                       /* Just under range */
+                       snd_ctl_elem_value_copy(val, ctl->def_val);
+                       snd_ctl_elem_value_set_integer64(val, i,
+                               snd_ctl_elem_info_get_min64(ctl->info) - 1);
+
+                       if (test_ctl_write_invalid_value(ctl, val))
+                               fail = true;
+
+                       /* Minimum representable value */
+                       snd_ctl_elem_value_copy(val, ctl->def_val);
+                       snd_ctl_elem_value_set_integer64(val, i, LLONG_MIN);
+
+                       if (test_ctl_write_invalid_value(ctl, val))
+                               fail = true;
+               }
+
+               if (snd_ctl_elem_info_get_max64(ctl->info) != LLONG_MAX) {
+                       /* Just over range */
+                       snd_ctl_elem_value_copy(val, ctl->def_val);
+                       snd_ctl_elem_value_set_integer64(val, i,
+                               snd_ctl_elem_info_get_max64(ctl->info) + 1);
+
+                       if (test_ctl_write_invalid_value(ctl, val))
+                               fail = true;
+
+                       /* Maximum representable value */
+                       snd_ctl_elem_value_copy(val, ctl->def_val);
+                       snd_ctl_elem_value_set_integer64(val, i, LLONG_MAX);
+
+                       if (test_ctl_write_invalid_value(ctl, val))
+                               fail = true;
+               }
+       }
+
+       return !fail;
+}
+
+static bool test_ctl_write_invalid_enumerated(struct ctl_data *ctl)
+{
+       int err, i;
+       unsigned int val_read;
+       bool fail = false;
+       snd_ctl_elem_value_t *val;
+       snd_ctl_elem_value_alloca(&val);
+
+       snd_ctl_elem_value_set_id(val, ctl->id);
+
+       for (i = 0; i < snd_ctl_elem_info_get_count(ctl->info); i++) {
+               /* One beyond maximum */
+               snd_ctl_elem_value_copy(val, ctl->def_val);
+               snd_ctl_elem_value_set_enumerated(val, i,
+                                 snd_ctl_elem_info_get_items(ctl->info));
+
+               if (test_ctl_write_invalid_value(ctl, val))
+                       fail = true;
+
+               /* Maximum representable value */
+               snd_ctl_elem_value_copy(val, ctl->def_val);
+               snd_ctl_elem_value_set_enumerated(val, i, UINT_MAX);
+
+               if (test_ctl_write_invalid_value(ctl, val))
+                       fail = true;
+
+       }
+
+       return !fail;
+}
+
+
+static void test_ctl_write_invalid(struct ctl_data *ctl)
+{
+       bool pass;
+       int err;
+
+       /* If the control is turned off let's be polite */
+       if (snd_ctl_elem_info_is_inactive(ctl->info)) {
+               ksft_print_msg("%s is inactive\n", ctl->name);
+               ksft_test_result_skip("write_invalid.%d.%d\n",
+                                     ctl->card->card, ctl->elem);
+               return;
+       }
+
+       if (!snd_ctl_elem_info_is_writable(ctl->info)) {
+               ksft_print_msg("%s is not writeable\n", ctl->name);
+               ksft_test_result_skip("write_invalid.%d.%d\n",
+                                     ctl->card->card, ctl->elem);
+               return;
+       }
+
+       switch (snd_ctl_elem_info_get_type(ctl->info)) {
+       case SND_CTL_ELEM_TYPE_BOOLEAN:
+               pass = test_ctl_write_invalid_boolean(ctl);
+               break;
+
+       case SND_CTL_ELEM_TYPE_INTEGER:
+               pass = test_ctl_write_invalid_integer(ctl);
+               break;
+
+       case SND_CTL_ELEM_TYPE_INTEGER64:
+               pass = test_ctl_write_invalid_integer64(ctl);
+               break;
+
+       case SND_CTL_ELEM_TYPE_ENUMERATED:
+               pass = test_ctl_write_invalid_enumerated(ctl);
+               break;
+
+       default:
+               /* No tests for this yet */
+               ksft_test_result_skip("write_invalid.%d.%d\n",
+                                     ctl->card->card, ctl->elem);
+               return;
+       }
+
+       /* Restore the default value to minimise disruption */
+       err = write_and_verify(ctl, ctl->def_val, NULL);
+       if (err < 0)
+               pass = false;
+
+       ksft_test_result(pass, "write_invalid.%d.%d\n",
+                        ctl->card->card, ctl->elem);
+}
+
+static void test_ctl_event_missing(struct ctl_data *ctl)
+{
+       ksft_test_result(!ctl->event_missing, "event_missing.%d.%d\n",
+                        ctl->card->card, ctl->elem);
+}
+
+static void test_ctl_event_spurious(struct ctl_data *ctl)
+{
+       ksft_test_result(!ctl->event_spurious, "event_spurious.%d.%d\n",
+                        ctl->card->card, ctl->elem);
+}
+
 int main(void)
 {
        struct ctl_data *ctl;
@@ -697,6 +1064,9 @@ int main(void)
                test_ctl_get_value(ctl);
                test_ctl_write_default(ctl);
                test_ctl_write_valid(ctl);
+               test_ctl_write_invalid(ctl);
+               test_ctl_event_missing(ctl);
+               test_ctl_event_spurious(ctl);
        }
 
        ksft_exit_pass();