[PATCH 3/3] ALSA: usb: add support for XMOS XVF3500

From: Javier Carrasco
Date: Mon Jan 15 2024 - 04:17:59 EST


The XMOS XVF3500 VocalFusion Voice Processor[1] is a low-latency, 32-bit
multicore controller for voice processing.

This simple driver provides the power sequence the device requires,
which consists of enabling the regulators that control the device
supplies and a reset de-assertion after a delay of at least 100ns.

Simple PM operations to handle the power sequence after resuming
from a power-down mode are also provided.

Once in normal operation, the device registers itself as a USB device.
Therefore, this driver requires USB to be available in order to
guarantee full support.

[1] https://www.xmos.com/xvf3500/

Signed-off-by: Javier Carrasco <javier.carrasco@xxxxxxxxxxxxxx>
---
MAINTAINERS | 7 +++
sound/usb/Kconfig | 9 +++
sound/usb/Makefile | 3 +-
sound/usb/xvf3500/Makefile | 4 ++
sound/usb/xvf3500/xvf3500.c | 140 ++++++++++++++++++++++++++++++++++++++++++++
5 files changed, 162 insertions(+), 1 deletion(-)

diff --git a/MAINTAINERS b/MAINTAINERS
index a7c4cf8201e0..fb9be0e12c71 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -23960,6 +23960,13 @@ S: Supported
W: http://www.marvell.com
F: drivers/i2c/busses/i2c-xlp9xx.c

+XMOS XVF3500 VOICE PROCESSOR DRIVER
+M: Javier Carrasco <javier.carrasco@xxxxxxxxxxxxxx>
+L: linux-sound@xxxxxxxxxxxxxxx
+S: Supported
+F: Documentation/devicetree/bindings/sound/xmos,xvf3500.yaml
+F: sound/usb/xvf3500/xvf3500.c
+
XRA1403 GPIO EXPANDER
M: Nandor Han <nandor.han@xxxxxx>
L: linux-gpio@xxxxxxxxxxxxxxx
diff --git a/sound/usb/Kconfig b/sound/usb/Kconfig
index 4a9569a3a39a..11565429163b 100644
--- a/sound/usb/Kconfig
+++ b/sound/usb/Kconfig
@@ -176,6 +176,15 @@ config SND_BCD2000
To compile this driver as a module, choose M here: the module
will be called snd-bcd2000.

+config SND_XVF3500
+ tristate "XMOS XVF3500 voice processor driver"
+ help
+ Say Y here to include support for the XMOS XVF3500 voice
+ processor.
+
+ To compile this driver as a module, choose M here: the module
+ will be called snd-xvf3500.
+
source "sound/usb/line6/Kconfig"

endif # SND_USB
diff --git a/sound/usb/Makefile b/sound/usb/Makefile
index 8c657c2753c8..4171db0f483c 100644
--- a/sound/usb/Makefile
+++ b/sound/usb/Makefile
@@ -34,5 +34,6 @@ obj-$(CONFIG_SND_USB_UA101) += snd-usbmidi-lib.o
obj-$(CONFIG_SND_USB_USX2Y) += snd-usbmidi-lib.o
obj-$(CONFIG_SND_USB_US122L) += snd-usbmidi-lib.o

-obj-$(CONFIG_SND) += misc/ usx2y/ caiaq/ 6fire/ hiface/ bcd2000/
+obj-$(CONFIG_SND) += misc/ usx2y/ caiaq/ 6fire/ hiface/ bcd2000/ xvf3500/
obj-$(CONFIG_SND_USB_LINE6) += line6/
+
diff --git a/sound/usb/xvf3500/Makefile b/sound/usb/xvf3500/Makefile
new file mode 100644
index 000000000000..51a61c8f165d
--- /dev/null
+++ b/sound/usb/xvf3500/Makefile
@@ -0,0 +1,4 @@
+# SPDX-License-Identifier: GPL-2.0-only
+snd-xvf3500-y := xvf3500.o
+
+obj-$(CONFIG_SND_XVF3500) += snd-xvf3500.o
diff --git a/sound/usb/xvf3500/xvf3500.c b/sound/usb/xvf3500/xvf3500.c
new file mode 100644
index 000000000000..647e5d09d1e5
--- /dev/null
+++ b/sound/usb/xvf3500/xvf3500.c
@@ -0,0 +1,140 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Driver for the XMOS XVF3500 VocalFusion Voice Processor.
+ *
+ * Copyright (C) 2023 WolfVision GmbH.
+ *
+ */
+
+#include <linux/gpio/consumer.h>
+#include <linux/module.h>
+#include <linux/of_platform.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+#include <linux/regulator/consumer.h>
+
+static const char * const supply_names[] = {
+ "vcc1v0",
+ "vcc3v3",
+};
+
+#define NUM_SUPPLIES ARRAY_SIZE(supply_names)
+
+struct xvf3500 {
+ struct regulator_bulk_data supplies[NUM_SUPPLIES];
+ struct device *dev;
+ struct gpio_desc *reset;
+};
+
+static int xvf3500_power(struct xvf3500 *priv, bool on)
+{
+ int ret;
+
+ if (on) {
+ ret = regulator_bulk_enable(NUM_SUPPLIES, priv->supplies);
+ if (ret) {
+ dev_err(priv->dev, "failed to enable supplies: %d\n", ret);
+ return ret;
+ }
+ /*
+ * A delay of >=100ns + regulator startup is needed before releasing
+ * the reset here. Wait for 10 ms to be on the safe side.
+ */
+ fsleep(10000);
+ gpiod_set_value_cansleep(priv->reset, 0);
+ } else {
+ gpiod_set_value_cansleep(priv->reset, 1);
+ ret = regulator_bulk_disable(NUM_SUPPLIES, priv->supplies);
+ if (ret) {
+ dev_err(priv->dev, "failed to disable supplies: %d\n", ret);
+ return ret;
+ }
+ }
+
+ return 0;
+}
+
+static int xvf3500_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct xvf3500 *priv;
+ int ret;
+
+ priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
+ priv->dev = dev;
+ dev_set_drvdata(dev, priv);
+
+ regulator_bulk_set_supply_names(priv->supplies, supply_names,
+ NUM_SUPPLIES);
+
+ ret = devm_regulator_bulk_get(dev, NUM_SUPPLIES, priv->supplies);
+ if (ret) {
+ dev_err_probe(dev, ret, "Failed to get regulator supplies\n");
+ return ret;
+ }
+
+ priv->reset = devm_gpiod_get(dev, "reset", GPIOD_OUT_HIGH);
+ if (IS_ERR(priv->reset))
+ return dev_err_probe(priv->dev, PTR_ERR(priv->reset),
+ "failed to get reset GPIO\n");
+
+ return xvf3500_power(priv, true);
+}
+
+static void xvf3500_remove(struct platform_device *pdev)
+{
+ struct xvf3500 *priv = dev_get_drvdata(&pdev->dev);
+
+ xvf3500_power(priv, false);
+}
+
+#ifdef CONFIG_PM_SLEEP
+static int xvf3500_suspend(struct device *dev)
+{
+ struct xvf3500 *priv = dev_get_drvdata(dev);
+
+ xvf3500_power(priv, false);
+
+ return 0;
+}
+
+static int xvf3500_resume(struct device *dev)
+{
+ struct xvf3500 *priv = dev_get_drvdata(dev);
+
+ xvf3500_power(priv, true);
+
+ return 0;
+}
+
+static SIMPLE_DEV_PM_OPS(xvf3500_pm, xvf3500_suspend, xvf3500_resume);
+#define XVF3500_PM_OPS (&xvf3500_pm)
+#else
+#define XVF3500_PM_OPS NULL
+#endif /* CONFIG_PM_SLEEP */
+
+static const struct of_device_id xvf3500_of_table[] = {
+ {
+ .compatible = "xmos,xvf3500",
+ },
+ {},
+};
+MODULE_DEVICE_TABLE(of, xvf3500_of_table);
+
+static struct platform_driver xvf3500_driver = {
+ .driver = {
+ .name = "xvf3500",
+ .of_match_table = xvf3500_of_table,
+ .pm = XVF3500_PM_OPS,
+ },
+ .probe = xvf3500_probe,
+ .remove_new = xvf3500_remove,
+};
+module_platform_driver(xvf3500_driver);
+
+MODULE_AUTHOR("Javier Carrasco <javier.carrasco@xxxxxxxxxxxxxx>");
+MODULE_DESCRIPTION("XMOS XVF3500 Voice Processor");
+MODULE_LICENSE("GPL");

--
2.39.2