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(-)
@@ -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
@@ -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
@@ -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/
+
new file mode 100644
@@ -0,0 +1,4 @@
+# SPDX-License-Identifier: GPL-2.0-only
+snd-xvf3500-y := xvf3500.o
+
+obj-$(CONFIG_SND_XVF3500) += snd-xvf3500.o
new file mode 100644
@@ -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");