diff mbox series

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

Message ID 20240115-feature-xvf3500_driver-v1-3-ed9cfb48bb85@wolfvision.net
State New
Headers show
Series ALSA: usb: add support for XMOS XVF3500 | expand

Commit Message

Javier Carrasco Jan. 15, 2024, 9:16 a.m. UTC
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@wolfvision.net>
---
 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 mbox series

Patch

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@wolfvision.net>
+L:	linux-sound@vger.kernel.org
+S:	Supported
+F:	Documentation/devicetree/bindings/sound/xmos,xvf3500.yaml
+F:	sound/usb/xvf3500/xvf3500.c
+
 XRA1403 GPIO EXPANDER
 M:	Nandor Han <nandor.han@ge.com>
 L:	linux-gpio@vger.kernel.org
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@wolfvision.net>");
+MODULE_DESCRIPTION("XMOS XVF3500 Voice Processor");
+MODULE_LICENSE("GPL");