From b4f742d2b6c718ab7f15414e9e6617c1afc7d6e7 Mon Sep 17 00:00:00 2001
From: Stephan Gerhold <stephan@gerhold.net>
Date: Tue, 28 Apr 2020 16:14:50 +0200
Subject: [PATCH 42/78] ASoC: qdsp6: Add Q6 Voice DAI driver (v2.1)

The Q6 Voice DAI driver exposes the Q6 Voice subsystem to ASoC.
At the moment it provides a single CS-Voice DAI with a hostless FE.
Eventually usage should be simplified using a codec2codec link.

v2: Set AIFs SND_SOC_NOPM
v2.1: Fix __DT_BINDINGS_Q6_VOCIE_H__ typo
---
 include/dt-bindings/sound/qcom,q6voice.h |   7 ++
 sound/soc/qcom/Kconfig                   |   4 +
 sound/soc/qcom/qdsp6/Makefile            |   1 +
 sound/soc/qcom/qdsp6/q6voice-dai.c       | 136 +++++++++++++++++++++++
 4 files changed, 148 insertions(+)
 create mode 100644 include/dt-bindings/sound/qcom,q6voice.h
 create mode 100644 sound/soc/qcom/qdsp6/q6voice-dai.c

diff --git a/include/dt-bindings/sound/qcom,q6voice.h b/include/dt-bindings/sound/qcom,q6voice.h
new file mode 100644
index 000000000..fe5aa3a3f
--- /dev/null
+++ b/include/dt-bindings/sound/qcom,q6voice.h
@@ -0,0 +1,7 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+#ifndef __DT_BINDINGS_Q6_VOICE_H__
+#define __DT_BINDINGS_Q6_VOICE_H__
+
+#define	CS_VOICE        0
+
+#endif /* __DT_BINDINGS_Q6_VOICE_H__ */
diff --git a/sound/soc/qcom/Kconfig b/sound/soc/qcom/Kconfig
index 6f5b85c35..70f525965 100644
--- a/sound/soc/qcom/Kconfig
+++ b/sound/soc/qcom/Kconfig
@@ -103,9 +103,13 @@ config SND_SOC_QDSP6
 	 audio drivers. This includes q6asm, q6adm,
 	 q6afe interfaces to DSP using apr.
 
+config SND_SOC_QDSP6_Q6VOICE_DAI
+	tristate
+
 config SND_SOC_QDSP6_Q6VOICE
 	tristate "QDSP6 Q6Voice audio driver"
 	depends on SND_SOC_QDSP6
+	select SND_SOC_QDSP6_Q6VOICE_DAI
 
 config SND_SOC_MSM8996
 	tristate "SoC Machine driver for MSM8996 and APQ8096 boards"
diff --git a/sound/soc/qcom/qdsp6/Makefile b/sound/soc/qcom/qdsp6/Makefile
index adc76ab35..eea7e1235 100644
--- a/sound/soc/qcom/qdsp6/Makefile
+++ b/sound/soc/qcom/qdsp6/Makefile
@@ -10,3 +10,4 @@ obj-$(CONFIG_SND_SOC_QDSP6_ASM) += q6asm.o
 obj-$(CONFIG_SND_SOC_QDSP6_ASM_DAI) += q6asm-dai.o
 obj-$(CONFIG_SND_SOC_QDSP6_Q6VOICE) += q6cvp.o q6cvs.o q6mvm.o q6voice-common.o
 obj-$(CONFIG_SND_SOC_QDSP6_Q6VOICE) += q6voice.o
+obj-$(CONFIG_SND_SOC_QDSP6_Q6VOICE_DAI) += q6voice-dai.o
diff --git a/sound/soc/qcom/qdsp6/q6voice-dai.c b/sound/soc/qcom/qdsp6/q6voice-dai.c
new file mode 100644
index 000000000..73401ed12
--- /dev/null
+++ b/sound/soc/qcom/qdsp6/q6voice-dai.c
@@ -0,0 +1,136 @@
+// SPDX-License-Identifier: GPL-2.0
+// Copyright (c) 2012-2017, The Linux Foundation. All rights reserved.
+// Copyright (c) 2020, Stephan Gerhold
+
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <sound/soc.h>
+#include <dt-bindings/sound/qcom,q6voice.h>
+#include "q6voice.h"
+
+#define DRV_NAME	"q6voice-dai"
+
+static int q6voice_dai_startup(struct snd_pcm_substream *substream,
+			       struct snd_soc_dai *dai)
+{
+	struct q6voice *v = snd_soc_dai_get_drvdata(dai);
+
+	return q6voice_start(v, Q6VOICE_PATH_VOICE, substream->stream);
+}
+
+static void q6voice_dai_shutdown(struct snd_pcm_substream *substream,
+				 struct snd_soc_dai *dai)
+{
+	struct q6voice *v = snd_soc_dai_get_drvdata(dai);
+
+	q6voice_stop(v, Q6VOICE_PATH_VOICE, substream->stream);
+}
+
+static struct snd_soc_dai_ops q6voice_dai_ops = {
+	.startup = q6voice_dai_startup,
+	.shutdown = q6voice_dai_shutdown,
+};
+
+static struct snd_soc_dai_driver q6voice_dais[] = {
+	{
+		.id = CS_VOICE,
+		.name = "CS-VOICE",
+		/* The constraints here are not really meaningful... */
+		.playback = {
+			.stream_name =	"CS-VOICE Playback",
+			.formats =	SNDRV_PCM_FMTBIT_S16_LE,
+			.rates =	SNDRV_PCM_RATE_8000,
+			.rate_min =	8000,
+			.rate_max =	8000,
+			.channels_min =	1,
+			.channels_max =	1,
+		},
+		.capture = {
+			.stream_name =	"CS-VOICE Capture",
+			.formats =	SNDRV_PCM_FMTBIT_S16_LE,
+			.rates =	SNDRV_PCM_RATE_8000,
+			.rate_min =	8000,
+			.rate_max =	8000,
+			.channels_min =	1,
+			.channels_max =	1,
+		},
+		.ops = &q6voice_dai_ops,
+	},
+};
+
+/* FIXME: Use codec2codec instead */
+static struct snd_pcm_hardware q6voice_dai_hardware = {
+	.info =			SNDRV_PCM_INFO_INTERLEAVED,
+	.buffer_bytes_max =	4096 * 2,
+	.period_bytes_min =	2048,
+	.period_bytes_max =	4096,
+	.periods_min =		2,
+	.periods_max =		4,
+	.fifo_size =		0,
+};
+
+static int q6voice_dai_open(struct snd_soc_component *component,
+			    struct snd_pcm_substream *substream)
+{
+	substream->runtime->hw = q6voice_dai_hardware;
+	return 0;
+}
+
+static const struct snd_soc_dapm_widget q6voice_dapm_widgets[] = {
+	SND_SOC_DAPM_AIF_IN("CS-VOICE_DL1", "CS-VOICE Playback", 0, SND_SOC_NOPM, 0, 0),
+	SND_SOC_DAPM_AIF_OUT("CS-VOICE_UL1", "CS-VOICE Capture", 0, SND_SOC_NOPM, 0, 0),
+};
+
+static const struct snd_soc_dapm_route q6voice_dapm_routes[] = {
+	/* TODO: Make routing configurable */
+	{"CS-VOICE_UL1", NULL, "TERT_MI2S_TX"},
+	{"PRI_MI2S_RX", NULL, "CS-VOICE_DL1"},
+};
+
+static const struct snd_soc_component_driver q6voice_dai_component = {
+	.name = DRV_NAME,
+	.open = q6voice_dai_open,
+
+	.dapm_widgets = q6voice_dapm_widgets,
+	.num_dapm_widgets = ARRAY_SIZE(q6voice_dapm_widgets),
+	.dapm_routes = q6voice_dapm_routes,
+	.num_dapm_routes = ARRAY_SIZE(q6voice_dapm_routes),
+
+	/* Needs to probe after q6afe */
+	.probe_order = SND_SOC_COMP_ORDER_LATE,
+};
+
+static int q6voice_dai_probe(struct platform_device *pdev)
+{
+	struct device *dev = &pdev->dev;
+	struct q6voice *v;
+
+	v = q6voice_create(dev);
+	if (IS_ERR(v))
+		return PTR_ERR(v);
+
+	dev_set_drvdata(dev, v);
+
+	return devm_snd_soc_register_component(dev, &q6voice_dai_component,
+					       q6voice_dais,
+					       ARRAY_SIZE(q6voice_dais));
+}
+
+static const struct of_device_id q6voice_dai_device_id[] = {
+	{ .compatible = "qcom,q6voice-dais" },
+	{},
+};
+MODULE_DEVICE_TABLE(of, q6voice_dai_device_id);
+
+static struct platform_driver q6voice_dai_platform_driver = {
+	.driver = {
+		.name = "q6voice-dai",
+		.of_match_table = of_match_ptr(q6voice_dai_device_id),
+	},
+	.probe = q6voice_dai_probe,
+};
+module_platform_driver(q6voice_dai_platform_driver);
+
+MODULE_AUTHOR("Stephan Gerhold <stephan@gerhold.net>");
+MODULE_DESCRIPTION("Q6Voice DAI driver");
+MODULE_LICENSE("GPL v2");
-- 
2.31.1

