summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Documentation/devicetree/bindings/sound/qcom-audio-dev.txt66
-rw-r--r--arch/arm/boot/dts/qcom/apq8096-v3-auto-adp.dts6
-rw-r--r--arch/arm/boot/dts/qcom/apq8096pro-v1.1-auto-adp-lite.dts4
-rw-r--r--arch/arm/boot/dts/qcom/apq8096pro-v1.1-auto-adp.dts4
-rw-r--r--arch/arm/boot/dts/qcom/msm8996-agave-adp.dtsi37
-rw-r--r--arch/arm/boot/dts/qcom/msm8996-v3-auto-adp.dts6
-rw-r--r--arch/arm/boot/dts/qcom/msm8996pro-auto-adp-lite.dts4
-rw-r--r--arch/arm/boot/dts/qcom/msm8996pro-auto-adp.dts4
-rw-r--r--drivers/soc/qcom/Kconfig7
-rw-r--r--drivers/soc/qcom/qdsp6v2/Makefile2
-rw-r--r--drivers/soc/qcom/qdsp6v2/audio-anc-dev-mgr.c1170
-rw-r--r--drivers/soc/qcom/qdsp6v2/audio_anc.c350
-rw-r--r--drivers/soc/qcom/qdsp6v2/sdsp-anc.c801
-rw-r--r--include/linux/qdsp6v2/audio-anc-dev-mgr.h46
-rw-r--r--include/linux/qdsp6v2/sdsp_anc.h302
-rw-r--r--include/uapi/linux/Kbuild1
-rw-r--r--include/uapi/linux/msm_audio_anc.h53
17 files changed, 2837 insertions, 26 deletions
diff --git a/Documentation/devicetree/bindings/sound/qcom-audio-dev.txt b/Documentation/devicetree/bindings/sound/qcom-audio-dev.txt
index b6d0c9affa0e..4cf7b93b922e 100644
--- a/Documentation/devicetree/bindings/sound/qcom-audio-dev.txt
+++ b/Documentation/devicetree/bindings/sound/qcom-audio-dev.txt
@@ -2550,3 +2550,69 @@ Example of child node that would have qcom,wdsp-cmpnt-dev-name property
wcd934x_cdc: tavil_codec {
qcom,wdsp-cmpnt-dev-name = "tavil_codec";
};
+
+
+* MSM external ANC driver
+
+Required properties:
+- compatible : "qcom,msm-ext-anc"
+- qcom,refs-port-id : This is AFE port ID for playback in ADSP used for ANC Algo refers input.
+- qcom,spkr-port-id : This is AFE port ID for ANC speaker in Sensor DSP.
+- qcom,mic-port-id : This is AFE port ID for ANC mic in Sensor DSP.
+- qcom,num-anc-mic : Define the number of microphones which are directly involved in ANC processing.
+- qcom,num-add-mic-signal : Define additional microphone which might be required for monitoring the environment, input reference signal.
+- qcom,anc-mic-array : Array that specifies the channel or slot index used for ANC in one MIC hardware interface like TDM Tx.
+ The channel or slot index is count from 0.
+ Always place the valid channel or slot index value setting in from index 0 of this array.
+ This array include two parts:
+ Part I ---- num_anc_mic, define the number of microphones which are directly involved in ANC processing.
+ Part II ---- num_add_mic_signal, define additional microphones which might be required for
+ monitoring the environment, input reference signal.
+ num_add_mic_signal is always appened at the end of num_anc_mic.
+- qcom,num-anc-spkr : Define the number of speakers which are directly involved in ANC processing.
+- qcom,num-add-spkr-signal : Define additional speaker channels which connects to interested speakers for example a subwoofer.
+- qcom,anc-spkr-array : Array that specifies the channel or slot index used for ANC in one SPEAKER hardware interface like TDM Rx.
+ The channel or slot index is count from 0.
+ Always place the valid channel or slot index value setting in from index 0 of this array.
+ This array include two parts:
+ Part I ---- num_anc_spkr, define the number of speakers which are directly involved in ANC processing.
+ Part II ---- num_add_spkr_signal, define additional speakers.
+ num_add_spkr_signal is always appened at the end of num_anc_spkr.
+- qcom,refs-tdm-rx : Point to phandle for refs tdm port info.
+- qcom,spkr-tdm-rx : Point to phandle for spkr tdm port info.
+- qcom,mic-tdm-tx : Point to phandle for mic tdm port info.
+Example 1:
+
+ qcom,msm-ext-anc {
+ compatible = "qcom,msm-ext-anc";
+ qcom,refs-port-id = <36906>;
+ qcom,spkr-port-id = <36912>;
+ qcom,mic-port-id = <36913>;
+ qcom,num-anc-mic = <4>;
+ qcom,num-add-mic-signal = <0>;
+ qcom,anc-mic-array = <0 1 2 3>;
+ qcom,num-anc-spkr = <4>;
+ qcom,num-add-spkr-signal = <0>;
+ qcom,anc-spkr-array = <0 1 2 3>;
+ qcom,refs-tdm-rx = <&dai_tert_tdm_rx_5>;
+ qcom,spkr-tdm-rx = <&dai_quat_tdm_rx_0>;
+ qcom,mic-tdm-tx = <&dai_quat_tdm_tx_0>;
+ };
+
+Example 2:
+
+ qcom,msm-ext-anc {
+ compatible = "qcom,msm-ext-anc";
+ qcom,refs-port-id = <36906>;
+ qcom,spkr-port-id = <36912>;
+ qcom,mic-port-id = <36913>;
+ qcom,num-anc-mic = <4>;
+ qcom,num-add-mic-signal = <2>;
+ qcom,anc-mic-array = <2 1 0 3 6 7>;
+ qcom,num-anc-spkr = <4>;
+ qcom,num-add-spkr-signal = <1>;
+ qcom,anc-spkr-array = <0 1 2 3 6>;
+ qcom,refs-tdm-rx = <&dai_tert_tdm_rx_5>;
+ qcom,spkr-tdm-rx = <&dai_quat_tdm_rx_0>;
+ qcom,mic-tdm-tx = <&dai_quat_tdm_tx_0>;
+ };
diff --git a/arch/arm/boot/dts/qcom/apq8096-v3-auto-adp.dts b/arch/arm/boot/dts/qcom/apq8096-v3-auto-adp.dts
index a91ec5eeb2e7..46894ea1e530 100644
--- a/arch/arm/boot/dts/qcom/apq8096-v3-auto-adp.dts
+++ b/arch/arm/boot/dts/qcom/apq8096-v3-auto-adp.dts
@@ -1,4 +1,4 @@
-/* Copyright (c) 2015-2016, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2015-2017, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -36,10 +36,6 @@
};
&soc {
- qcom,msm-ssc-sensors {
- status = "disabled";
- };
-
qcom,msm-thermal {
qcom,hotplug-temp = <115>;
qcom,hotplug-temp-hysteresis = <25>;
diff --git a/arch/arm/boot/dts/qcom/apq8096pro-v1.1-auto-adp-lite.dts b/arch/arm/boot/dts/qcom/apq8096pro-v1.1-auto-adp-lite.dts
index 6c2413d98efd..e4eca6aed98b 100644
--- a/arch/arm/boot/dts/qcom/apq8096pro-v1.1-auto-adp-lite.dts
+++ b/arch/arm/boot/dts/qcom/apq8096pro-v1.1-auto-adp-lite.dts
@@ -30,10 +30,6 @@
};
&soc {
- qcom,msm-ssc-sensors {
- status = "disabled";
- };
-
qcom,msm-thermal {
qcom,hotplug-temp = <115>;
qcom,hotplug-temp-hysteresis = <25>;
diff --git a/arch/arm/boot/dts/qcom/apq8096pro-v1.1-auto-adp.dts b/arch/arm/boot/dts/qcom/apq8096pro-v1.1-auto-adp.dts
index eb24e7198f87..39ec8d4cb591 100644
--- a/arch/arm/boot/dts/qcom/apq8096pro-v1.1-auto-adp.dts
+++ b/arch/arm/boot/dts/qcom/apq8096pro-v1.1-auto-adp.dts
@@ -37,10 +37,6 @@
};
&soc {
- qcom,msm-ssc-sensors {
- status = "disabled";
- };
-
qcom,msm-thermal {
qcom,hotplug-temp = <115>;
qcom,hotplug-temp-hysteresis = <25>;
diff --git a/arch/arm/boot/dts/qcom/msm8996-agave-adp.dtsi b/arch/arm/boot/dts/qcom/msm8996-agave-adp.dtsi
index 2adfb8b749eb..b1de96051b00 100644
--- a/arch/arm/boot/dts/qcom/msm8996-agave-adp.dtsi
+++ b/arch/arm/boot/dts/qcom/msm8996-agave-adp.dtsi
@@ -1041,6 +1041,12 @@
status = "disabled";
};
+ qcom,msm-ssc-sensors {
+ compatible = "qcom,msm-ssc-sensors";
+ qcom,firmware-name = "slpi";
+ status = "ok";
+ };
+
sound-adp-agave {
compatible = "qcom,apq8096-asoc-snd-adp-agave";
qcom,model = "apq8096-adp-agave-snd-card";
@@ -1107,6 +1113,37 @@
asoc-codec-names = "msm-stub-codec.1";
};
+ qcom,msm-dai-tdm-tert-rx {
+ qcom,msm-cpudai-tdm-group-num-ports = <6>;
+ qcom,msm-cpudai-tdm-group-port-id = <36896 36898 36900
+ 36902 36904 36906>;
+
+ dai_tert_tdm_rx_5: qcom,msm-dai-q6-tdm-tert-rx-5 {
+ compatible = "qcom,msm-dai-q6-tdm";
+ qcom,msm-cpudai-tdm-dev-id = <36906>;
+ qcom,msm-cpudai-tdm-data-align = <0>;
+ };
+ };
+
+ qcom,msm-ext-anc {
+ compatible = "qcom,msm-ext-anc";
+ qcom,refs-port-id = <36906>;
+ qcom,spkr-port-id = <36912>;
+ qcom,mic-port-id = <36913>;
+ qcom,sample-rate = <48000>;
+ qcom,num-channels = <8>;
+ qcom,bit-width = <32>;
+ qcom,num-anc-mic = <4>;
+ qcom,num-add-mic-signal = <0>;
+ qcom,anc-mic-array = <0 1 2 3>;
+ qcom,num-anc-spkr = <4>;
+ qcom,num-add-spkr-signal = <0>;
+ qcom,anc-spkr-array = <0 1 2 3>;
+ qcom,refs-tdm-rx = <&dai_tert_tdm_rx_5>;
+ qcom,spkr-tdm-rx = <&dai_quat_tdm_rx_0>;
+ qcom,mic-tdm-tx = <&dai_quat_tdm_tx_0>;
+ };
+
usb_detect: usb_detect {
compatible = "qcom,gpio-usbdetect";
qcom,vbus-det-gpio = <&pm8994_gpios 17 0>;
diff --git a/arch/arm/boot/dts/qcom/msm8996-v3-auto-adp.dts b/arch/arm/boot/dts/qcom/msm8996-v3-auto-adp.dts
index 68956d71b74d..89a585bd426e 100644
--- a/arch/arm/boot/dts/qcom/msm8996-v3-auto-adp.dts
+++ b/arch/arm/boot/dts/qcom/msm8996-v3-auto-adp.dts
@@ -1,4 +1,4 @@
-/* Copyright (c) 2015-2016, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2015-2017, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -37,10 +37,6 @@
};
&soc {
- qcom,msm-ssc-sensors {
- status = "disabled";
- };
-
qcom,msm-thermal {
qcom,hotplug-temp = <115>;
qcom,hotplug-temp-hysteresis = <25>;
diff --git a/arch/arm/boot/dts/qcom/msm8996pro-auto-adp-lite.dts b/arch/arm/boot/dts/qcom/msm8996pro-auto-adp-lite.dts
index 9fd2686dac67..02f5dbcc0d4d 100644
--- a/arch/arm/boot/dts/qcom/msm8996pro-auto-adp-lite.dts
+++ b/arch/arm/boot/dts/qcom/msm8996pro-auto-adp-lite.dts
@@ -30,10 +30,6 @@
};
&soc {
- qcom,msm-ssc-sensors {
- status = "disabled";
- };
-
qcom,msm-thermal {
qcom,hotplug-temp = <115>;
qcom,hotplug-temp-hysteresis = <25>;
diff --git a/arch/arm/boot/dts/qcom/msm8996pro-auto-adp.dts b/arch/arm/boot/dts/qcom/msm8996pro-auto-adp.dts
index ec1b7cec4147..05a3144fb312 100644
--- a/arch/arm/boot/dts/qcom/msm8996pro-auto-adp.dts
+++ b/arch/arm/boot/dts/qcom/msm8996pro-auto-adp.dts
@@ -37,10 +37,6 @@
};
&soc {
- qcom,msm-ssc-sensors {
- status = "disabled";
- };
-
qcom,msm-thermal {
qcom,hotplug-temp = <115>;
qcom,hotplug-temp-hysteresis = <25>;
diff --git a/drivers/soc/qcom/Kconfig b/drivers/soc/qcom/Kconfig
index 1e8f50c4ebad..b92c217dc1b5 100644
--- a/drivers/soc/qcom/Kconfig
+++ b/drivers/soc/qcom/Kconfig
@@ -967,4 +967,11 @@ config QCOM_QDSS_BRIDGE
sub-system to USB on APSS side. The driver acts as a bridge between the
MHI and USB interface. If unsure, say N.
+config EXT_ANC
+ bool "Enable External ANC"
+ depends on MSM_QDSP6_APRV2 || MSM_QDSP6_APRV3
+ help
+ This option enables support for anti-noise cnacellation
+ on Sensor DSP.
+
source "drivers/soc/qcom/memshare/Kconfig"
diff --git a/drivers/soc/qcom/qdsp6v2/Makefile b/drivers/soc/qcom/qdsp6v2/Makefile
index 90feb8b659d1..0a0e258e6ec1 100644
--- a/drivers/soc/qcom/qdsp6v2/Makefile
+++ b/drivers/soc/qcom/qdsp6v2/Makefile
@@ -10,3 +10,5 @@ obj-$(CONFIG_MSM_QDSP6_SSR) += audio_ssr.o
obj-$(CONFIG_MSM_QDSP6_PDR) += audio_pdr.o
obj-$(CONFIG_MSM_QDSP6_NOTIFIER) += audio_notifier.o
obj-$(CONFIG_MSM_CDSP_LOADER) += cdsp-loader.o
+obj-$(CONFIG_EXT_ANC) += sdsp-anc.o audio_anc.o audio-anc-dev-mgr.o
+
diff --git a/drivers/soc/qcom/qdsp6v2/audio-anc-dev-mgr.c b/drivers/soc/qcom/qdsp6v2/audio-anc-dev-mgr.c
new file mode 100644
index 000000000000..75b114e6905c
--- /dev/null
+++ b/drivers/soc/qcom/qdsp6v2/audio-anc-dev-mgr.c
@@ -0,0 +1,1170 @@
+/* Copyright (c) 2018, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/device.h>
+#include <linux/platform_device.h>
+#include <linux/mfd/wcd9xxx/core.h>
+#include <linux/bitops.h>
+#include <linux/slab.h>
+#include <linux/clk.h>
+#include <linux/of_device.h>
+#include <linux/clk/msm-clk.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/soc.h>
+#include <sound/apr_audio-v2.h>
+#include <sound/q6afe-v2.h>
+#include <sound/msm-dai-q6-v2.h>
+#include <linux/qdsp6v2/audio-anc-dev-mgr.h>
+#include <linux/qdsp6v2/sdsp_anc.h>
+
+#define LPM_START_ADDR (0x9120000 + 60*1024)
+#define LPM_LENGTH (4*1024)
+
+enum {
+ ANC_DEV_PORT_REFS = 0,
+ ANC_DEV_PORT_ANC_SPKR,
+ ANC_DEV_PORT_ANC_MIC,
+ ANC_DEV_PORT_MAX,
+};
+
+struct anc_tdm_port_cfg_info {
+ u16 port_id;
+ struct afe_param_id_tdm_cfg port_cfg;
+};
+
+struct anc_tdm_group_set_info {
+ struct afe_param_id_group_device_tdm_cfg gp_cfg;
+ uint32_t num_tdm_group_ports;
+ struct afe_clk_set tdm_clk_set;
+ uint32_t clk_mode;
+};
+
+struct anc_dev_drv_info {
+ uint32_t state;
+ uint32_t rpm;
+ uint32_t bypass_mode;
+ uint32_t algo_module_id;
+};
+
+struct anc_dev_port_cfg_info {
+ uint32_t port_id;
+ uint32_t sample_rate;
+ uint32_t num_channels;
+ uint32_t bit_width;
+};
+
+static struct aud_msvc_param_id_dev_anc_mic_spkr_layout_info
+ anc_mic_spkr_layout;
+
+static struct anc_dev_port_cfg_info anc_port_cfg[ANC_DEV_PORT_MAX];
+
+static struct anc_tdm_group_set_info anc_dev_tdm_gp_set[IDX_GROUP_TDM_MAX];
+
+static struct anc_tdm_port_cfg_info anc_dev_tdm_port_cfg[IDX_TDM_MAX];
+
+static struct anc_dev_drv_info this_anc_dev_info;
+
+static int anc_dev_get_free_tdm_gp_cfg_idx(void)
+{
+ int idx = -1;
+ int i;
+
+ for (i = 0; i < IDX_GROUP_TDM_MAX; i++) {
+ if (anc_dev_tdm_gp_set[i].gp_cfg.group_id == 0) {
+ idx = i;
+ break;
+ }
+ }
+
+ return idx;
+}
+
+static int anc_dev_get_free_tdm_port_cfg_idx(void)
+{
+ int idx = -1;
+ int i;
+
+ for (i = 0; i < IDX_TDM_MAX; i++) {
+ if (anc_dev_tdm_port_cfg[i].port_id == 0) {
+ idx = i;
+ break;
+ }
+ }
+
+ return idx;
+}
+
+static u16 get_group_id_from_port_id(int32_t port_id)
+{
+ u16 gp_id = AFE_PORT_INVALID;
+
+ switch (port_id) {
+ case AFE_PORT_ID_PRIMARY_TDM_RX:
+ case AFE_PORT_ID_PRIMARY_TDM_RX_1:
+ case AFE_PORT_ID_PRIMARY_TDM_RX_2:
+ case AFE_PORT_ID_PRIMARY_TDM_RX_3:
+ case AFE_PORT_ID_PRIMARY_TDM_RX_4:
+ case AFE_PORT_ID_PRIMARY_TDM_RX_5:
+ case AFE_PORT_ID_PRIMARY_TDM_RX_6:
+ case AFE_PORT_ID_PRIMARY_TDM_RX_7:
+ gp_id = AFE_GROUP_DEVICE_ID_PRIMARY_TDM_RX;
+ break;
+ case AFE_PORT_ID_SECONDARY_TDM_RX:
+ case AFE_PORT_ID_SECONDARY_TDM_RX_1:
+ case AFE_PORT_ID_SECONDARY_TDM_RX_2:
+ case AFE_PORT_ID_SECONDARY_TDM_RX_3:
+ case AFE_PORT_ID_SECONDARY_TDM_RX_4:
+ case AFE_PORT_ID_SECONDARY_TDM_RX_5:
+ case AFE_PORT_ID_SECONDARY_TDM_RX_6:
+ case AFE_PORT_ID_SECONDARY_TDM_RX_7:
+ gp_id = AFE_GROUP_DEVICE_ID_SECONDARY_TDM_RX;
+ break;
+ case AFE_PORT_ID_TERTIARY_TDM_RX:
+ case AFE_PORT_ID_TERTIARY_TDM_RX_1:
+ case AFE_PORT_ID_TERTIARY_TDM_RX_2:
+ case AFE_PORT_ID_TERTIARY_TDM_RX_3:
+ case AFE_PORT_ID_TERTIARY_TDM_RX_4:
+ case AFE_PORT_ID_TERTIARY_TDM_RX_5:
+ case AFE_PORT_ID_TERTIARY_TDM_RX_6:
+ case AFE_PORT_ID_TERTIARY_TDM_RX_7:
+ gp_id = AFE_GROUP_DEVICE_ID_TERTIARY_TDM_RX;
+ break;
+ case AFE_PORT_ID_QUATERNARY_TDM_RX:
+ case AFE_PORT_ID_QUATERNARY_TDM_RX_1:
+ case AFE_PORT_ID_QUATERNARY_TDM_RX_2:
+ case AFE_PORT_ID_QUATERNARY_TDM_RX_3:
+ case AFE_PORT_ID_QUATERNARY_TDM_RX_4:
+ case AFE_PORT_ID_QUATERNARY_TDM_RX_5:
+ case AFE_PORT_ID_QUATERNARY_TDM_RX_6:
+ case AFE_PORT_ID_QUATERNARY_TDM_RX_7:
+ gp_id = AFE_GROUP_DEVICE_ID_QUATERNARY_TDM_RX;
+ break;
+ default:
+ break;
+ }
+
+ return gp_id;
+}
+
+static int anc_dev_get_matched_tdm_gp_cfg_idx(u16 gp_id)
+{
+ int idx = -1;
+ int i;
+
+ for (i = 0; i < IDX_GROUP_TDM_MAX; i++) {
+ if (anc_dev_tdm_gp_set[i].gp_cfg.group_id == gp_id) {
+ idx = i;
+ break;
+ }
+ }
+
+ return idx;
+}
+
+static int anc_dev_get_matched_tdm_port_cfg_idx(u16 port_id)
+{
+ int idx = -1;
+ int i;
+
+ for (i = 0; i < IDX_TDM_MAX; i++) {
+ if (anc_dev_tdm_port_cfg[i].port_id == port_id) {
+ idx = i;
+ break;
+ }
+ }
+
+ return idx;
+}
+
+static int anc_dev_tdm_set_clk(
+ struct anc_tdm_group_set_info *gp_set_data,
+ u16 port_id, bool enable)
+{
+ int rc = 0;
+
+ switch (gp_set_data->gp_cfg.group_id) {
+ case AFE_GROUP_DEVICE_ID_PRIMARY_TDM_RX:
+ case AFE_GROUP_DEVICE_ID_PRIMARY_TDM_TX:
+ if (gp_set_data->clk_mode) {
+ gp_set_data->tdm_clk_set.clk_id =
+ Q6AFE_LPASS_CLK_ID_PRI_TDM_IBIT;
+ } else
+ gp_set_data->tdm_clk_set.clk_id =
+ Q6AFE_LPASS_CLK_ID_PRI_TDM_EBIT;
+ break;
+ case AFE_GROUP_DEVICE_ID_SECONDARY_TDM_RX:
+ case AFE_GROUP_DEVICE_ID_SECONDARY_TDM_TX:
+ if (gp_set_data->clk_mode) {
+ gp_set_data->tdm_clk_set.clk_id =
+ Q6AFE_LPASS_CLK_ID_SEC_TDM_IBIT;
+ } else
+ gp_set_data->tdm_clk_set.clk_id =
+ Q6AFE_LPASS_CLK_ID_SEC_TDM_EBIT;
+ break;
+ case AFE_GROUP_DEVICE_ID_TERTIARY_TDM_RX:
+ case AFE_GROUP_DEVICE_ID_TERTIARY_TDM_TX:
+ if (gp_set_data->clk_mode) {
+ gp_set_data->tdm_clk_set.clk_id =
+ Q6AFE_LPASS_CLK_ID_TER_TDM_IBIT;
+ } else
+ gp_set_data->tdm_clk_set.clk_id =
+ Q6AFE_LPASS_CLK_ID_TER_TDM_EBIT;
+ break;
+ case AFE_GROUP_DEVICE_ID_QUATERNARY_TDM_RX:
+ case AFE_GROUP_DEVICE_ID_QUATERNARY_TDM_TX:
+ if (gp_set_data->clk_mode) {
+ gp_set_data->tdm_clk_set.clk_id =
+ Q6AFE_LPASS_CLK_ID_QUAD_TDM_IBIT;
+ } else
+ gp_set_data->tdm_clk_set.clk_id =
+ Q6AFE_LPASS_CLK_ID_QUAD_TDM_EBIT;
+ break;
+ default:
+ pr_err("%s: port id 0x%x not supported\n",
+ __func__, port_id);
+ return -EINVAL;
+ }
+ gp_set_data->tdm_clk_set.enable = enable;
+
+ rc = afe_set_lpass_clock_v2(port_id,
+ &gp_set_data->tdm_clk_set);
+
+ if (rc < 0)
+ pr_err("%s: afe lpass clock failed, err:%d\n",
+ __func__, rc);
+
+ return rc;
+}
+
+static int anc_dev_port_start(int32_t which_port)
+{
+ int rc = 0;
+ int pt_idx;
+
+ struct afe_tdm_port_config tdm_cfg;
+
+ pt_idx =
+ anc_dev_get_matched_tdm_port_cfg_idx(anc_port_cfg[which_port].port_id);
+
+ if (pt_idx == -1) {
+ rc = -EINVAL;
+ goto rtn;
+ }
+
+ tdm_cfg.tdm = anc_dev_tdm_port_cfg[pt_idx].port_cfg;
+
+ tdm_cfg.tdm.num_channels = anc_port_cfg[which_port].num_channels;
+ tdm_cfg.tdm.sample_rate = anc_port_cfg[which_port].sample_rate;
+ tdm_cfg.tdm.bit_width = anc_port_cfg[which_port].bit_width;
+
+ tdm_cfg.tdm.nslots_per_frame = anc_port_cfg[which_port].num_channels;
+ tdm_cfg.tdm.slot_width = anc_port_cfg[which_port].bit_width;
+ tdm_cfg.tdm.slot_mask =
+ ((1 << anc_port_cfg[which_port].num_channels) - 1);
+
+ pr_debug("%s: port_id %x num_channels %x bit_width %x sample_rate %x nslots_per_frame %x slot_width %x slot_mask %x!\n",
+ __func__,
+ anc_port_cfg[which_port].port_id,
+ tdm_cfg.tdm.num_channels,
+ tdm_cfg.tdm.bit_width,
+ tdm_cfg.tdm.sample_rate,
+ tdm_cfg.tdm.nslots_per_frame,
+ tdm_cfg.tdm.slot_width,
+ tdm_cfg.tdm.slot_mask);
+
+ rc = anc_if_tdm_port_start(anc_port_cfg[which_port].port_id,
+ &tdm_cfg);
+ if (IS_ERR_VALUE(rc)) {
+ pr_err("%s: fail to open ANC port from SDSP 0x%x\n",
+ __func__, anc_port_cfg[which_port].port_id);
+ goto rtn;
+ }
+
+rtn:
+ return rc;
+}
+
+static int anc_dev_port_stop(int32_t which_port)
+{
+ int rc = 0;
+
+ rc = anc_if_tdm_port_stop(anc_port_cfg[which_port].port_id);
+ if (IS_ERR_VALUE(rc)) {
+ pr_err("%s: fail to stop ANC port from SDSP 0x%x\n",
+ __func__, anc_port_cfg[which_port].port_id);
+ }
+
+ return rc;
+}
+
+int msm_anc_dev_set_info(void *info_p, int32_t anc_cmd)
+{
+ int rc = 0;
+
+ switch (anc_cmd) {
+ case ANC_CMD_RPM: {
+ struct audio_anc_rpm_info *rpm_info_p =
+ (struct audio_anc_rpm_info *)info_p;
+
+ if (this_anc_dev_info.state)
+ rc = anc_if_set_rpm(
+ anc_port_cfg[ANC_DEV_PORT_ANC_SPKR].port_id,
+ rpm_info_p->rpm);
+ else
+ this_anc_dev_info.rpm = 0;
+ break;
+ }
+ case ANC_CMD_BYPASS_MODE: {
+ struct audio_anc_bypass_mode *bypass_mode_p =
+ (struct audio_anc_bypass_mode *)info_p;
+
+ if (this_anc_dev_info.state)
+ rc = anc_if_set_bypass_mode(
+ anc_port_cfg[ANC_DEV_PORT_ANC_SPKR].port_id,
+ bypass_mode_p->mode);
+ else
+ this_anc_dev_info.bypass_mode = bypass_mode_p->mode;
+ break;
+ }
+ case ANC_CMD_ALGO_MODULE: {
+ struct audio_anc_algo_module_info *module_info_p =
+ (struct audio_anc_algo_module_info *)info_p;
+
+ if (this_anc_dev_info.state)
+ rc = anc_if_set_algo_module_id(
+ anc_port_cfg[ANC_DEV_PORT_ANC_SPKR].port_id,
+ module_info_p->module_id);
+ else
+ this_anc_dev_info.algo_module_id =
+ module_info_p->module_id;
+ break;
+ }
+ }
+
+ return rc;
+}
+
+
+int msm_anc_dev_start(void)
+{
+ int rc = 0;
+ u16 group_id;
+ int gp_idx, pt_idx;
+ union afe_port_group_config anc_dev_gp_cfg;
+ struct afe_tdm_port_config tdm_cfg;
+
+ pr_debug("%s: ANC devices start in!\n", __func__);
+
+ memset(&tdm_cfg, 0, sizeof(tdm_cfg));
+
+ /*
+ * Refs port for ADSP
+ * 1. enable clk
+ * 2. group cfg and enable
+ * 3. Refs port cfg and start
+ */
+
+ group_id =
+ get_group_id_from_port_id(anc_port_cfg[ANC_DEV_PORT_REFS].port_id);
+
+ gp_idx = anc_dev_get_matched_tdm_gp_cfg_idx(group_id);
+
+ if (gp_idx == -1) {
+ rc = -EINVAL;
+ pr_err("%s: anc_dev_get_matched_tdm_gp_cfg_idx() failed with group_id 0x%x\n",
+ __func__, group_id);
+ goto rtn;
+ } else {
+ rc = anc_dev_tdm_set_clk(&anc_dev_tdm_gp_set[gp_idx],
+ (u16)anc_port_cfg[ANC_DEV_PORT_REFS].port_id, true);
+
+ if (IS_ERR_VALUE(rc)) {
+ pr_err("%s: fail to enable AFE clk 0x%x\n",
+ __func__,
+ anc_port_cfg[ANC_DEV_PORT_REFS].port_id);
+ goto rtn;
+ }
+
+ anc_dev_gp_cfg.tdm_cfg = anc_dev_tdm_gp_set[gp_idx].gp_cfg;
+
+ anc_dev_gp_cfg.tdm_cfg.group_device_cfg_minor_version =
+ AFE_API_VERSION_GROUP_DEVICE_TDM_CONFIG;
+ anc_dev_gp_cfg.tdm_cfg.num_channels =
+ anc_port_cfg[ANC_DEV_PORT_REFS].num_channels;
+ anc_dev_gp_cfg.tdm_cfg.bit_width =
+ anc_port_cfg[ANC_DEV_PORT_REFS].bit_width;
+ anc_dev_gp_cfg.tdm_cfg.sample_rate =
+ anc_port_cfg[ANC_DEV_PORT_REFS].sample_rate;
+ anc_dev_gp_cfg.tdm_cfg.nslots_per_frame =
+ anc_port_cfg[ANC_DEV_PORT_REFS].num_channels;
+ anc_dev_gp_cfg.tdm_cfg.slot_width =
+ anc_port_cfg[ANC_DEV_PORT_REFS].bit_width;
+ anc_dev_gp_cfg.tdm_cfg.slot_mask =
+ ((1 << anc_port_cfg[ANC_DEV_PORT_REFS].num_channels) - 1);
+
+ pr_debug("%s: refs_port_id %x\n", __func__,
+ anc_port_cfg[ANC_DEV_PORT_REFS].port_id);
+
+ pr_debug("%s: anc_dev_gp_cfg num_channels %x bit_width %x sample_rate %x nslots_per_frame %x slot_width %x slot_mask %x!\n",
+ __func__,
+ anc_dev_gp_cfg.tdm_cfg.num_channels,
+ anc_dev_gp_cfg.tdm_cfg.bit_width,
+ anc_dev_gp_cfg.tdm_cfg.sample_rate,
+ anc_dev_gp_cfg.tdm_cfg.nslots_per_frame,
+ anc_dev_gp_cfg.tdm_cfg.slot_width,
+ anc_dev_gp_cfg.tdm_cfg.slot_mask);
+
+ rc = afe_port_group_enable(group_id,
+ &anc_dev_gp_cfg, true);
+ if (IS_ERR_VALUE(rc)) {
+ pr_err("%s: fail to enable AFE group 0x%x\n",
+ __func__, group_id);
+ goto rtn;
+ }
+
+ pt_idx =
+ anc_dev_get_matched_tdm_port_cfg_idx(
+ anc_port_cfg[ANC_DEV_PORT_REFS].port_id);
+
+ if (pt_idx == -1) {
+ rc = -EINVAL;
+ pr_err("%s: anc_dev_get_matched_tdm_port_cfg_idx() failed with port_id 0x%x\n",
+ __func__,
+ anc_port_cfg[ANC_DEV_PORT_REFS].port_id);
+ goto rtn;
+ }
+
+ tdm_cfg.tdm = anc_dev_tdm_port_cfg[pt_idx].port_cfg;
+
+ tdm_cfg.tdm.num_channels =
+ anc_port_cfg[ANC_DEV_PORT_REFS].num_channels;
+ tdm_cfg.tdm.sample_rate =
+ anc_port_cfg[ANC_DEV_PORT_REFS].sample_rate;
+ tdm_cfg.tdm.bit_width =
+ anc_port_cfg[ANC_DEV_PORT_REFS].bit_width;
+
+ tdm_cfg.tdm.nslots_per_frame =
+ anc_dev_gp_cfg.tdm_cfg.nslots_per_frame;
+ tdm_cfg.tdm.slot_width = anc_dev_gp_cfg.tdm_cfg.slot_width;
+ tdm_cfg.tdm.slot_mask = anc_dev_gp_cfg.tdm_cfg.slot_mask;
+
+ rc = afe_tdm_port_start(anc_port_cfg[ANC_DEV_PORT_REFS].port_id,
+ &tdm_cfg,
+ anc_port_cfg[ANC_DEV_PORT_REFS].sample_rate, 0);
+ if (IS_ERR_VALUE(rc)) {
+ afe_port_group_enable(group_id,
+ &anc_dev_gp_cfg, false);
+
+ anc_dev_tdm_set_clk(&anc_dev_tdm_gp_set[gp_idx],
+ (u16)anc_port_cfg[ANC_DEV_PORT_REFS].port_id, false);
+
+ pr_err("%s: fail to open AFE port 0x%x\n",
+ __func__,
+ anc_port_cfg[ANC_DEV_PORT_REFS].port_id);
+ goto rtn;
+ }
+
+ }
+
+ rc = anc_if_set_anc_mic_spkr_layout(
+ anc_port_cfg[ANC_DEV_PORT_REFS].port_id,
+ &anc_mic_spkr_layout);
+ if (IS_ERR_VALUE(rc)) {
+ pr_err("%s: fail to pass ANC MIC and SPKR layout info to SDSP 0x%x\n",
+ __func__,
+ anc_port_cfg[ANC_DEV_PORT_REFS].port_id);
+ goto rtn;
+ }
+
+ rc = anc_if_share_resource(
+ anc_port_cfg[ANC_DEV_PORT_REFS].port_id, 4, 3,
+ LPM_START_ADDR, LPM_LENGTH);
+ if (IS_ERR_VALUE(rc)) {
+ pr_err("%s: fail to assign lpass resource to SDSP 0x%x\n",
+ __func__,
+ anc_port_cfg[ANC_DEV_PORT_REFS].port_id);
+ goto rtn;
+ }
+
+ rc = anc_if_config_ref(anc_port_cfg[ANC_DEV_PORT_REFS].port_id,
+ anc_port_cfg[ANC_DEV_PORT_REFS].sample_rate,
+ anc_port_cfg[ANC_DEV_PORT_REFS].bit_width,
+ anc_port_cfg[ANC_DEV_PORT_REFS].num_channels);
+ if (IS_ERR_VALUE(rc)) {
+ pr_err("%s: fail to refs port cfg in SDSP 0x%x\n",
+ __func__,
+ anc_port_cfg[ANC_DEV_PORT_REFS].port_id);
+ goto rtn;
+ }
+
+ if (this_anc_dev_info.algo_module_id != 0)
+ rc = anc_if_set_algo_module_id(
+ anc_port_cfg[ANC_DEV_PORT_ANC_SPKR].port_id,
+ this_anc_dev_info.algo_module_id);
+
+ if (this_anc_dev_info.bypass_mode != 0)
+ rc = anc_if_set_bypass_mode(
+ anc_port_cfg[ANC_DEV_PORT_ANC_SPKR].port_id,
+ this_anc_dev_info.bypass_mode);
+
+ group_id = get_group_id_from_port_id(
+ anc_port_cfg[ANC_DEV_PORT_ANC_SPKR].port_id);
+
+ gp_idx = anc_dev_get_matched_tdm_gp_cfg_idx(group_id);
+
+ if (gp_idx == -1) {
+ rc = -EINVAL;
+ pr_err("%s: anc_dev_get_matched_tdm_gp_cfg_idx() failed with group_id 0x%x\n",
+ __func__, group_id);
+ goto rtn;
+ } else {
+ rc = anc_dev_tdm_set_clk(&anc_dev_tdm_gp_set[gp_idx],
+ (u16)anc_port_cfg[ANC_DEV_PORT_ANC_SPKR].port_id, true);
+
+ if (IS_ERR_VALUE(rc)) {
+ pr_err("%s: fail to enable AFE clk 0x%x\n",
+ __func__,
+ anc_port_cfg[ANC_DEV_PORT_ANC_SPKR].port_id);
+ goto rtn;
+ }
+ }
+
+ rc = anc_dev_port_start(ANC_DEV_PORT_ANC_MIC);
+ if (IS_ERR_VALUE(rc)) {
+ pr_err("%s: fail to enable ANC MIC Port 0x%x\n",
+ __func__,
+ anc_port_cfg[ANC_DEV_PORT_ANC_MIC].port_id);
+ goto rtn;
+ }
+
+ rc = anc_dev_port_start(ANC_DEV_PORT_ANC_SPKR);
+ if (IS_ERR_VALUE(rc)) {
+ pr_err("%s: fail to enable ANC SPKR Port 0x%x\n",
+ __func__,
+ anc_port_cfg[ANC_DEV_PORT_ANC_SPKR].port_id);
+ goto rtn;
+ }
+
+ this_anc_dev_info.state = 1;
+
+ pr_debug("%s: ANC devices start successfully!\n", __func__);
+
+rtn:
+ return rc;
+}
+
+int msm_anc_dev_stop(void)
+{
+ int rc = 0;
+ u16 group_id;
+ int gp_idx;
+
+ anc_dev_port_stop(ANC_DEV_PORT_ANC_SPKR);
+ anc_dev_port_stop(ANC_DEV_PORT_ANC_MIC);
+
+ group_id = get_group_id_from_port_id(
+ anc_port_cfg[ANC_DEV_PORT_ANC_SPKR].port_id);
+
+ gp_idx = anc_dev_get_matched_tdm_gp_cfg_idx(group_id);
+
+ if (gp_idx == -1) {
+ rc = -EINVAL;
+ goto rtn;
+ } else {
+ rc = anc_dev_tdm_set_clk(&anc_dev_tdm_gp_set[gp_idx],
+ (u16)anc_port_cfg[ANC_DEV_PORT_ANC_SPKR].port_id, false);
+
+ if (IS_ERR_VALUE(rc)) {
+ pr_err("%s: fail to disable AFE clk 0x%x\n",
+ __func__,
+ anc_port_cfg[ANC_DEV_PORT_ANC_SPKR].port_id);
+ }
+ }
+
+ group_id =
+ get_group_id_from_port_id(anc_port_cfg[ANC_DEV_PORT_REFS].port_id);
+
+ gp_idx = anc_dev_get_matched_tdm_gp_cfg_idx(group_id);
+
+ if (gp_idx == -1) {
+ rc = -EINVAL;
+ goto rtn;
+ }
+
+ afe_close(anc_port_cfg[ANC_DEV_PORT_REFS].port_id);
+
+ afe_port_group_enable(group_id, NULL, false);
+
+ anc_dev_tdm_set_clk(&anc_dev_tdm_gp_set[gp_idx],
+ (u16)anc_port_cfg[ANC_DEV_PORT_REFS].port_id, false);
+
+ this_anc_dev_info.state = 0;
+ this_anc_dev_info.algo_module_id = 0;
+ this_anc_dev_info.rpm = 0;
+ this_anc_dev_info.bypass_mode = 0;
+
+ pr_debug("%s: ANC devices stop successfully!\n", __func__);
+
+rtn:
+ return rc;
+}
+
+
+static int msm_anc_tdm_dev_group_cfg_info(
+ struct platform_device *pdev,
+ struct device_node *ctx_node)
+{
+ int rc = 0;
+ const uint32_t *port_id_array = NULL;
+ uint32_t num_tdm_group_ports = 0;
+ uint32_t array_length = 0;
+ int i = 0;
+ int gp_idx = anc_dev_get_free_tdm_gp_cfg_idx();
+
+ if ((gp_idx < 0) || (gp_idx > IDX_GROUP_TDM_MAX)) {
+ dev_err(&pdev->dev, "%s: could not get abaiable tdm group cfg slot\n",
+ __func__);
+ rc = -EINVAL;
+ goto rtn;
+ }
+
+ /* extract tdm group info into static */
+ rc = of_property_read_u32(ctx_node,
+ "qcom,msm-cpudai-tdm-group-id",
+ (u32 *)&anc_dev_tdm_gp_set[gp_idx].gp_cfg.group_id);
+ if (rc) {
+ dev_err(&pdev->dev, "%s: Group ID from DT file %s\n",
+ __func__, "qcom,msm-cpudai-tdm-group-id");
+ goto rtn;
+ }
+
+ dev_dbg(&pdev->dev, "%s: dev_name: %s group_id: 0x%x\n",
+ __func__, dev_name(&pdev->dev),
+ anc_dev_tdm_gp_set[gp_idx].gp_cfg.group_id);
+
+ rc = of_property_read_u32(ctx_node,
+ "qcom,msm-cpudai-tdm-group-num-ports",
+ &num_tdm_group_ports);
+ if (rc) {
+ dev_err(&pdev->dev, "%s: Group Num Ports from DT file %s\n",
+ __func__, "qcom,msm-cpudai-tdm-group-num-ports");
+ goto rtn;
+ }
+ dev_dbg(&pdev->dev, "%s: Group Num Ports from DT file 0x%x\n",
+ __func__, num_tdm_group_ports);
+
+ if (num_tdm_group_ports > AFE_GROUP_DEVICE_NUM_PORTS) {
+ dev_err(&pdev->dev, "%s Group Num Ports %d greater than Max %d\n",
+ __func__, num_tdm_group_ports,
+ AFE_GROUP_DEVICE_NUM_PORTS);
+ rc = -EINVAL;
+ goto rtn;
+ }
+
+ port_id_array = of_get_property(ctx_node,
+ "qcom,msm-cpudai-tdm-group-port-id",
+ &array_length);
+ if (port_id_array == NULL) {
+ dev_err(&pdev->dev, "%s port_id_array is not valid\n",
+ __func__);
+ rc = -EINVAL;
+ goto rtn;
+ }
+ if (array_length != sizeof(uint32_t) * num_tdm_group_ports) {
+ dev_err(&pdev->dev, "%s array_length is %d, expected is %zd\n",
+ __func__, array_length,
+ sizeof(uint32_t) * num_tdm_group_ports);
+ rc = -EINVAL;
+ goto rtn;
+ }
+
+ for (i = 0; i < num_tdm_group_ports; i++)
+ anc_dev_tdm_gp_set[gp_idx].gp_cfg.port_id[i] =
+ (u16)be32_to_cpu(port_id_array[i]);
+ /* Unused index should be filled with 0 or AFE_PORT_INVALID */
+ for (i = num_tdm_group_ports;
+ i < AFE_GROUP_DEVICE_NUM_PORTS; i++)
+ anc_dev_tdm_gp_set[gp_idx].gp_cfg.port_id[i] = AFE_PORT_INVALID;
+
+ anc_dev_tdm_gp_set[gp_idx].num_tdm_group_ports = num_tdm_group_ports;
+
+ /* extract tdm clk info into static */
+ rc = of_property_read_u32(ctx_node,
+ "qcom,msm-cpudai-tdm-clk-rate",
+ &anc_dev_tdm_gp_set[gp_idx].tdm_clk_set.clk_freq_in_hz);
+ if (rc) {
+ dev_err(&pdev->dev, "%s: Clk Rate from DT file %s\n",
+ __func__, "qcom,msm-cpudai-tdm-clk-rate");
+ goto rtn;
+ }
+ dev_dbg(&pdev->dev, "%s: Clk Rate from DT file %d\n",
+ __func__,
+ anc_dev_tdm_gp_set[gp_idx].tdm_clk_set.clk_freq_in_hz);
+
+ anc_dev_tdm_gp_set[gp_idx].tdm_clk_set.clk_set_minor_version =
+ Q6AFE_LPASS_CLK_CONFIG_API_VERSION;
+ anc_dev_tdm_gp_set[gp_idx].tdm_clk_set.clk_attri =
+ Q6AFE_LPASS_CLK_ATTRIBUTE_INVERT_COUPLE_NO;
+ anc_dev_tdm_gp_set[gp_idx].tdm_clk_set.clk_root =
+ Q6AFE_LPASS_CLK_ROOT_DEFAULT;
+
+
+ /* extract tdm clk attribute into static */
+ if (of_find_property(ctx_node,
+ "qcom,msm-cpudai-tdm-clk-attribute", NULL)) {
+ rc = of_property_read_u16(ctx_node,
+ "qcom,msm-cpudai-tdm-clk-attribute",
+ &anc_dev_tdm_gp_set[gp_idx].tdm_clk_set.clk_attri);
+ if (rc) {
+ dev_err(&pdev->dev, "%s: No Clk attribute in DT file %s\n",
+ __func__,
+ "qcom,msm-cpudai-tdm-clk-attribute");
+ goto rtn;
+ }
+ } else {
+ dev_dbg(&pdev->dev, "%s: Clk Attribute from DT file %d\n",
+ __func__,
+ anc_dev_tdm_gp_set[gp_idx].tdm_clk_set.clk_attri);
+ }
+
+ /* extract tdm clk src master/slave info into static */
+ rc = of_property_read_u32(ctx_node,
+ "qcom,msm-cpudai-tdm-clk-internal",
+ &anc_dev_tdm_gp_set[gp_idx].clk_mode);
+ if (rc) {
+ dev_err(&pdev->dev, "%s: Clk id from DT file %s\n",
+ __func__, "qcom,msm-cpudai-tdm-clk-internal");
+ goto rtn;
+ }
+ dev_dbg(&pdev->dev, "%s: Clk id from DT file %d\n",
+ __func__, anc_dev_tdm_gp_set[gp_idx].clk_mode);
+
+rtn:
+ return rc;
+}
+
+
+static int msm_anc_tdm_dev_port_cfg_info(
+ struct platform_device *pdev,
+ struct device_node *ctx_node)
+{
+ int rc = 0;
+ u32 tdm_dev_id = 0;
+ int pt_idx = anc_dev_get_free_tdm_port_cfg_idx();
+ struct device_node *tdm_parent_node = NULL;
+
+ if ((pt_idx < 0) || (pt_idx > IDX_TDM_MAX)) {
+ dev_err(&pdev->dev, "%s: could not get abaiable tdm port cfg slot\n",
+ __func__);
+ rc = -EINVAL;
+ goto rtn;
+ }
+
+ /* retrieve device/afe id */
+ rc = of_property_read_u32(ctx_node,
+ "qcom,msm-cpudai-tdm-dev-id",
+ &tdm_dev_id);
+ if (rc) {
+ dev_err(&pdev->dev, "%s: Device ID missing in DT file\n",
+ __func__);
+ goto rtn;
+ }
+ if ((tdm_dev_id < AFE_PORT_ID_TDM_PORT_RANGE_START) ||
+ (tdm_dev_id > AFE_PORT_ID_TDM_PORT_RANGE_END)) {
+ dev_err(&pdev->dev, "%s: Invalid TDM Device ID 0x%x in DT file\n",
+ __func__, tdm_dev_id);
+ rc = -ENXIO;
+ goto rtn;
+ }
+ anc_dev_tdm_port_cfg[pt_idx].port_id = tdm_dev_id;
+
+ dev_dbg(&pdev->dev, "%s: dev_name: %s dev_id: 0x%x\n",
+ __func__, dev_name(&pdev->dev), tdm_dev_id);
+
+ /* TDM CFG */
+ tdm_parent_node = of_get_parent(ctx_node);
+ rc = of_property_read_u32(tdm_parent_node,
+ "qcom,msm-cpudai-tdm-sync-mode",
+ (u32 *)&anc_dev_tdm_port_cfg[pt_idx].port_cfg.sync_mode);
+ if (rc) {
+ dev_err(&pdev->dev, "%s: Sync Mode from DT file %s\n",
+ __func__, "qcom,msm-cpudai-tdm-sync-mode");
+ goto rtn;
+ }
+ dev_dbg(&pdev->dev, "%s: Sync Mode from DT file 0x%x\n",
+ __func__, anc_dev_tdm_port_cfg[pt_idx].port_cfg.sync_mode);
+
+ rc = of_property_read_u32(tdm_parent_node,
+ "qcom,msm-cpudai-tdm-sync-src",
+ (u32 *)&anc_dev_tdm_port_cfg[pt_idx].port_cfg.sync_src);
+ if (rc) {
+ dev_err(&pdev->dev, "%s: Sync Src from DT file %s\n",
+ __func__, "qcom,msm-cpudai-tdm-sync-src");
+ goto rtn;
+ }
+ dev_dbg(&pdev->dev, "%s: Sync Src from DT file 0x%x\n",
+ __func__, anc_dev_tdm_port_cfg[pt_idx].port_cfg.sync_src);
+
+ rc = of_property_read_u32(tdm_parent_node,
+ "qcom,msm-cpudai-tdm-data-out",
+ (u32 *)&anc_dev_tdm_port_cfg[pt_idx].port_cfg.ctrl_data_out_enable);
+ if (rc) {
+ dev_err(&pdev->dev, "%s: Data Out from DT file %s\n",
+ __func__, "qcom,msm-cpudai-tdm-data-out");
+ goto rtn;
+ }
+ dev_dbg(&pdev->dev, "%s: Data Out from DT file 0x%x\n",
+ __func__,
+ anc_dev_tdm_port_cfg[pt_idx].port_cfg.ctrl_data_out_enable);
+
+ rc = of_property_read_u32(tdm_parent_node,
+ "qcom,msm-cpudai-tdm-invert-sync",
+ (u32 *)&anc_dev_tdm_port_cfg[pt_idx].port_cfg.ctrl_invert_sync_pulse);
+ if (rc) {
+ dev_err(&pdev->dev, "%s: Invert Sync from DT file %s\n",
+ __func__, "qcom,msm-cpudai-tdm-invert-sync");
+ goto rtn;
+ }
+ dev_dbg(&pdev->dev, "%s: Invert Sync from DT file 0x%x\n",
+ __func__,
+ anc_dev_tdm_port_cfg[pt_idx].port_cfg.ctrl_invert_sync_pulse);
+
+ rc = of_property_read_u32(tdm_parent_node,
+ "qcom,msm-cpudai-tdm-data-delay",
+ (u32 *)&anc_dev_tdm_port_cfg[pt_idx].port_cfg.ctrl_sync_data_delay);
+ if (rc) {
+ dev_err(&pdev->dev, "%s: Data Delay from DT file %s\n",
+ __func__, "qcom,msm-cpudai-tdm-data-delay");
+ goto rtn;
+ }
+ dev_dbg(&pdev->dev, "%s: Data Delay from DT file 0x%x\n",
+ __func__,
+ anc_dev_tdm_port_cfg[pt_idx].port_cfg.ctrl_sync_data_delay);
+
+ /* TDM CFG -- set default */
+ anc_dev_tdm_port_cfg[pt_idx].port_cfg.data_format = AFE_LINEAR_PCM_DATA;
+ anc_dev_tdm_port_cfg[pt_idx].port_cfg.tdm_cfg_minor_version =
+ AFE_API_VERSION_TDM_CONFIG;
+
+ msm_anc_tdm_dev_group_cfg_info(pdev, tdm_parent_node);
+
+ return 0;
+
+rtn:
+ return rc;
+}
+
+static int msm_anc_dev_probe(struct platform_device *pdev)
+{
+ int rc = 0;
+
+ u32 port_id = 0;
+ const uint32_t *layout_array = NULL;
+ uint32_t num_anc_io = 0;
+ uint32_t array_length = 0;
+ int i = 0;
+ uint32_t sample_rate = 0;
+ uint32_t num_channels = 0;
+ uint32_t bit_width = 0;
+
+ rc = of_property_read_u32(pdev->dev.of_node,
+ "qcom,refs-port-id",
+ (u32 *)&port_id);
+ if (rc) {
+ dev_err(&pdev->dev, "%s: ANC refs-port-id DT file %s\n",
+ __func__, "qcom,refs-port-id");
+ goto rtn;
+ }
+
+ anc_port_cfg[ANC_DEV_PORT_REFS].port_id = port_id;
+
+ dev_dbg(&pdev->dev, "%s: refs-port-id 0x%x\n",
+ __func__, port_id);
+
+ port_id = 0;
+ rc = of_property_read_u32(pdev->dev.of_node,
+ "qcom,spkr-port-id",
+ (u32 *)&port_id);
+ if (rc) {
+ dev_err(&pdev->dev, "%s: ANC spkr-port-id DT file %s\n",
+ __func__, "qcom,spkr-port-id");
+ goto rtn;
+ }
+
+ anc_port_cfg[ANC_DEV_PORT_ANC_SPKR].port_id = port_id;
+
+ dev_dbg(&pdev->dev, "%s: spkr-port-id 0x%x\n",
+ __func__, port_id);
+
+ port_id = 0;
+ rc = of_property_read_u32(pdev->dev.of_node,
+ "qcom,mic-port-id",
+ (u32 *)&port_id);
+ if (rc) {
+ dev_err(&pdev->dev, "%s: ANC mic-port-id DT file %s\n",
+ __func__, "qcom,mic-port-id");
+ goto rtn;
+ }
+
+ anc_port_cfg[ANC_DEV_PORT_ANC_MIC].port_id = port_id;
+
+ dev_dbg(&pdev->dev, "%s: mic-port-id 0x%x\n",
+ __func__, port_id);
+
+ rc = of_property_read_u32(pdev->dev.of_node,
+ "qcom,sample-rate",
+ (u32 *)&sample_rate);
+ if (rc) {
+ dev_err(&pdev->dev, "%s: ANC sample rate DT file %s\n",
+ __func__, "qcom,sample-rate");
+ goto rtn;
+ }
+
+ dev_dbg(&pdev->dev, "%s: ANC sample rate 0x%x\n",
+ __func__, sample_rate);
+
+ rc = of_property_read_u32(pdev->dev.of_node,
+ "qcom,num-channels",
+ (u32 *)&num_channels);
+ if (rc) {
+ dev_err(&pdev->dev, "%s: ANC num channels DT file %s\n",
+ __func__, "qcom,num-channels");
+ goto rtn;
+ }
+
+ dev_dbg(&pdev->dev, "%s: ANC num channel 0x%x\n",
+ __func__, num_channels);
+
+ rc = of_property_read_u32(pdev->dev.of_node,
+ "qcom,bit-width",
+ (u32 *)&bit_width);
+ if (rc) {
+ dev_err(&pdev->dev, "%s: ANC bit width DT file %s\n",
+ __func__, "qcom,bit-width");
+ goto rtn;
+ }
+
+ dev_dbg(&pdev->dev, "%s: ANC bit width 0x%x\n",
+ __func__, bit_width);
+
+ for (i = 0; i < ANC_DEV_PORT_MAX; i++) {
+ anc_port_cfg[i].sample_rate = sample_rate;
+ anc_port_cfg[i].num_channels = num_channels;
+ anc_port_cfg[i].bit_width = bit_width;
+ }
+
+ memset(&anc_mic_spkr_layout, 0, sizeof(anc_mic_spkr_layout));
+
+ anc_mic_spkr_layout.minor_version = 1;
+
+ rc = of_property_read_u32(pdev->dev.of_node,
+ "qcom,num-anc-mic",
+ (u32 *)&num_anc_io);
+ if (rc) {
+ dev_err(&pdev->dev, "%s: ANC num_anc_mic DT file %s\n",
+ __func__, "qcom,num-anc-mic");
+ goto rtn;
+ }
+
+ layout_array = of_get_property(pdev->dev.of_node,
+ "qcom,anc-mic-array",
+ &array_length);
+ if (layout_array == NULL) {
+ dev_err(&pdev->dev, "%s layout_array is not valid\n",
+ __func__);
+ rc = -EINVAL;
+ goto rtn;
+ }
+ if (array_length != sizeof(uint32_t) * num_anc_io) {
+ dev_err(&pdev->dev, "%s array_length is %d, expected is %zd\n",
+ __func__, array_length,
+ sizeof(uint32_t) * num_anc_io);
+ rc = -EINVAL;
+ goto rtn;
+ }
+
+ anc_mic_spkr_layout.num_anc_mic = num_anc_io;
+
+ for (i = 0; i < num_anc_io; i++)
+ anc_mic_spkr_layout.mic_layout_array[i] =
+ (u16)be32_to_cpu(layout_array[i]);
+
+ num_anc_io = 0;
+ rc = of_property_read_u32(pdev->dev.of_node,
+ "qcom,num-anc-spkr",
+ (u32 *)&num_anc_io);
+ if (rc) {
+ dev_err(&pdev->dev, "%s: ANC num_anc_mic DT file %s\n",
+ __func__, "qcom,num-anc-spkr");
+ goto rtn;
+ }
+
+ layout_array = of_get_property(pdev->dev.of_node,
+ "qcom,anc-spkr-array",
+ &array_length);
+ if (layout_array == NULL) {
+ dev_err(&pdev->dev, "%s layout_array is not valid\n",
+ __func__);
+ rc = -EINVAL;
+ goto rtn;
+ }
+ if (array_length != sizeof(uint32_t) * num_anc_io) {
+ dev_err(&pdev->dev, "%s array_length is %d, expected is %zd\n",
+ __func__, array_length,
+ sizeof(uint32_t) * num_anc_io);
+ rc = -EINVAL;
+ goto rtn;
+ }
+
+ anc_mic_spkr_layout.num_anc_spkr = num_anc_io;
+
+ for (i = 0; i < num_anc_io; i++)
+ anc_mic_spkr_layout.spkr_layout_array[i] =
+ (u16)be32_to_cpu(layout_array[i]);
+
+ dev_dbg(&pdev->dev, "%s: num_anc_mic 0x%x\n",
+ __func__, anc_mic_spkr_layout.num_anc_mic);
+
+ dev_dbg(&pdev->dev, "%s: num_anc_spkr 0x%x\n",
+ __func__, anc_mic_spkr_layout.num_anc_spkr);
+
+ num_anc_io = 0;
+ rc = of_property_read_u32(pdev->dev.of_node,
+ "qcom,num-add-mic-signal",
+ (u32 *)&num_anc_io);
+ if (rc) {
+ dev_err(&pdev->dev, "%s: ANC num_add_mic_signal DT file %s\n",
+ __func__, "qcom,num-add-mic-signal");
+ goto rtn;
+ }
+
+ anc_mic_spkr_layout.num_add_mic_signal = num_anc_io;
+
+ num_anc_io = 0;
+ rc = of_property_read_u32(pdev->dev.of_node,
+ "qcom,num-add-spkr-signal",
+ (u32 *)&num_anc_io);
+ if (rc) {
+ dev_err(&pdev->dev, "%s: ANC num_add_spkr_signal DT file %s\n",
+ __func__, "qcom,num-add-spkr-signal");
+ goto rtn;
+ }
+
+ anc_mic_spkr_layout.num_add_spkr_signal = num_anc_io;
+
+ dev_dbg(&pdev->dev, "%s: num_add_mic_signal 0x%x\n",
+ __func__, anc_mic_spkr_layout.num_add_mic_signal);
+
+ dev_dbg(&pdev->dev, "%s: num_add_spkr_signal 0x%x\n",
+ __func__, anc_mic_spkr_layout.num_add_spkr_signal);
+
+ /* TDM group CFG and TDM port CFG */
+ {
+ struct device_node *ctx_node = NULL;
+
+ ctx_node = of_parse_phandle(pdev->dev.of_node,
+ "qcom,refs-tdm-rx", 0);
+ if (!ctx_node) {
+ pr_err("%s Could not find refs-tdm-rx info\n",
+ __func__);
+ return -EINVAL;
+ }
+
+ rc = msm_anc_tdm_dev_port_cfg_info(pdev, ctx_node);
+ if (IS_ERR_VALUE(rc)) {
+ pr_err("%s: fail to probe TDM group info\n",
+ __func__);
+ }
+
+ ctx_node = of_parse_phandle(pdev->dev.of_node,
+ "qcom,spkr-tdm-rx", 0);
+ if (!ctx_node) {
+ pr_err("%s Could not find spkr-tdm-rx info\n",
+ __func__);
+ return -EINVAL;
+ }
+
+ rc = msm_anc_tdm_dev_port_cfg_info(pdev, ctx_node);
+ if (IS_ERR_VALUE(rc)) {
+ pr_err("%s: fail to probe TDM group info\n",
+ __func__);
+ }
+
+ ctx_node = of_parse_phandle(pdev->dev.of_node,
+ "qcom,mic-tdm-tx", 0);
+ if (!ctx_node) {
+ pr_err("%s Could not find mic-tdm-tx info\n",
+ __func__);
+ return -EINVAL;
+ }
+
+ rc = msm_anc_tdm_dev_port_cfg_info(pdev, ctx_node);
+ if (IS_ERR_VALUE(rc)) {
+ pr_err("%s: fail to probe TDM group info\n",
+ __func__);
+ }
+ }
+
+ rc = msm_anc_dev_create(pdev);
+
+rtn:
+ return rc;
+}
+
+static int msm_anc_dev_remove(struct platform_device *pdev)
+{
+ return msm_anc_dev_destroy(pdev);
+}
+
+static const struct of_device_id msm_anc_dev_dt_match[] = {
+ { .compatible = "qcom,msm-ext-anc", },
+ {}
+};
+
+MODULE_DEVICE_TABLE(of, msm_anc_dev_dt_match);
+
+static struct platform_driver msm_anc_dev = {
+ .probe = msm_anc_dev_probe,
+ .remove = msm_anc_dev_remove,
+ .driver = {
+ .name = "msm-ext-anc",
+ .owner = THIS_MODULE,
+ .of_match_table = msm_anc_dev_dt_match,
+ },
+};
+
+int msm_anc_dev_init(void)
+{
+ int rc = 0;
+
+ memset(&anc_dev_tdm_gp_set, 0, sizeof(anc_dev_tdm_gp_set));
+ memset(&anc_dev_tdm_port_cfg, 0, sizeof(anc_dev_tdm_port_cfg));
+ memset(&anc_port_cfg, 0, sizeof(anc_port_cfg));
+ memset(&this_anc_dev_info, 0, sizeof(this_anc_dev_info));
+
+ rc = platform_driver_register(&msm_anc_dev);
+ if (rc)
+ pr_err("%s: fail to register msm ANC device driver\n",
+ __func__);
+
+ return rc;
+}
+
+int msm_anc_dev_deinit(void)
+{
+ platform_driver_unregister(&msm_anc_dev);
+ return 0;
+}
+
diff --git a/drivers/soc/qcom/qdsp6v2/audio_anc.c b/drivers/soc/qcom/qdsp6v2/audio_anc.c
new file mode 100644
index 000000000000..e0abd2b58027
--- /dev/null
+++ b/drivers/soc/qcom/qdsp6v2/audio_anc.c
@@ -0,0 +1,350 @@
+/* Copyright (c) 2018, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/slab.h>
+#include <linux/fs.h>
+#include <linux/module.h>
+#include <linux/uaccess.h>
+#include <linux/mutex.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/fs.h>
+#include <linux/cdev.h>
+#include <linux/uaccess.h>
+#include <linux/device.h>
+
+#include <linux/qdsp6v2/audio-anc-dev-mgr.h>
+
+#define DEVICE_NAME "msm_audio_anc"
+
+struct audio_anc_info {
+ struct cdev myc;
+ struct class *anc_class;
+};
+
+static int major;
+
+static struct audio_anc_info audio_anc;
+
+static size_t get_user_anc_cmd_size(int32_t anc_cmd)
+{
+ size_t size = 0;
+
+ switch (anc_cmd) {
+ case ANC_CMD_START:
+ case ANC_CMD_STOP:
+ size = 0;
+ break;
+ case ANC_CMD_RPM:
+ size = sizeof(struct audio_anc_rpm_info);
+ break;
+ case ANC_CMD_BYPASS_MODE:
+ size = sizeof(struct audio_anc_bypass_mode);
+ break;
+ case ANC_CMD_ALGO_MODULE:
+ size = sizeof(struct audio_anc_algo_module_info);
+ break;
+ default:
+ pr_err("%s:Invalid anc cmd %d!",
+ __func__, anc_cmd);
+ }
+ return size;
+}
+
+static int call_set_anc(int32_t anc_cmd,
+ size_t anc_cmd_size, void *data)
+{
+ int ret = 0;
+
+ pr_err("%s EXT_ANC anc_cmd %x\n", __func__, anc_cmd);
+
+ switch (anc_cmd) {
+ case ANC_CMD_START:
+ ret = msm_anc_dev_start();
+ break;
+ case ANC_CMD_STOP:
+ ret = msm_anc_dev_stop();
+ break;
+ case ANC_CMD_RPM:
+ case ANC_CMD_BYPASS_MODE:
+ case ANC_CMD_ALGO_MODULE:
+ ret = msm_anc_dev_set_info(data, anc_cmd);
+ break;
+ default:
+ break;
+ }
+
+ pr_err("%s EXT_ANC ret %x\n", __func__, ret);
+
+ return ret;
+}
+
+static int call_get_anc(int32_t anc_cmd,
+ size_t anc_cmd_size, void *data)
+{
+ int ret = 0;
+
+ switch (anc_cmd) {
+ case ANC_CMD_RPM:
+ break;
+ default:
+ break;
+ }
+
+ return ret;
+}
+
+static int audio_anc_open(struct inode *inode, struct file *f)
+{
+ int ret = 0;
+
+ pr_debug("%s\n", __func__);
+ return ret;
+}
+
+static int audio_anc_close(struct inode *inode, struct file *f)
+{
+ int ret = 0;
+
+ pr_debug("%s\n", __func__);
+ return ret;
+}
+
+static long audio_anc_shared_ioctl(struct file *file, unsigned int cmd,
+ void __user *arg)
+{
+ int ret = 0;
+ int32_t size;
+ struct audio_anc_packet *data = NULL;
+
+ pr_err("%s EXT_ANC cmd %x\n", __func__, cmd);
+
+ switch (cmd) {
+ case AUDIO_ANC_SET_PARAM:
+ case AUDIO_ANC_GET_PARAM:
+ break;
+ default:
+ pr_err("%s: ioctl not found!\n", __func__);
+ ret = -EFAULT;
+ goto done;
+ }
+
+ if (copy_from_user(&size, (void *)arg, sizeof(size))) {
+ pr_err("%s: Could not copy size value from user\n", __func__);
+ ret = -EFAULT;
+ goto done;
+ } else if (size < sizeof(struct audio_anc_packet)) {
+ pr_err("%s: Invalid size sent to driver: %d, min size is %zd\n",
+ __func__, size, sizeof(struct audio_anc_packet));
+ ret = -EINVAL;
+ goto done;
+ }
+
+ data = kmalloc(size, GFP_KERNEL);
+ if (data == NULL) {
+ ret = -ENOMEM;
+ pr_err("%s: Could not allocate memory of size %d for ioctl\n",
+ __func__, size);
+ goto done;
+ } else if (copy_from_user(data, (void *)arg, size)) {
+ pr_err("%s: Could not copy data from user\n",
+ __func__);
+ ret = -EFAULT;
+ goto done;
+ } else if ((data->hdr.anc_cmd < 0) ||
+ (data->hdr.anc_cmd >= ANC_CMD_MAX)) {
+ pr_err("%s: anc_cmd %d is Invalid!\n",
+ __func__, data->hdr.anc_cmd);
+ ret = -EINVAL;
+ goto done;
+ } else if ((data->hdr.anc_cmd_size <
+ get_user_anc_cmd_size(data->hdr.anc_cmd)) ||
+ (data->hdr.anc_cmd_size >
+ sizeof(union audio_anc_data))) {
+ pr_err("%s: anc_cmd size %d is Invalid! Min is %zd Max is %zd!\n",
+ __func__, data->hdr.anc_cmd_size,
+ get_user_anc_cmd_size(data->hdr.anc_cmd),
+ sizeof(union audio_anc_data));
+ ret = -EINVAL;
+ goto done;
+ }
+
+ switch (cmd) {
+ case AUDIO_ANC_SET_PARAM:
+ ret = call_set_anc(data->hdr.anc_cmd,
+ data->hdr.anc_cmd_size, &data->anc_data);
+ break;
+ case AUDIO_ANC_GET_PARAM:
+ ret = call_get_anc(data->hdr.anc_cmd,
+ data->hdr.anc_cmd_size, &data->anc_data);
+ break;
+ }
+
+ if (cmd == AUDIO_ANC_GET_PARAM) {
+ if (data->hdr.anc_cmd_size == 0)
+ goto done;
+ if (data == NULL)
+ goto done;
+ if ((sizeof(data->hdr) + data->hdr.anc_cmd_size) > size) {
+ pr_err("%s: header size %zd plus ype size %d larger than data buffer size %d\n",
+ __func__, sizeof(data->hdr),
+ data->hdr.anc_cmd_size, size);
+ ret = -EFAULT;
+ goto done;
+ } else if (copy_to_user((void *)arg, data,
+ sizeof(data->hdr) + data->hdr.anc_cmd_size)) {
+ pr_err("%s: Could not copy cal type to user\n",
+ __func__);
+ ret = -EFAULT;
+ goto done;
+ }
+ }
+
+done:
+ kfree(data);
+
+ pr_err("%s EXT_ANC ret %x\n", __func__, ret);
+
+ return ret;
+}
+
+static long audio_anc_ioctl(struct file *f,
+ unsigned int cmd, unsigned long arg)
+{
+ return audio_anc_shared_ioctl(f, cmd, (void __user *)arg);
+}
+
+#ifdef CONFIG_COMPAT
+
+#define AUDIO_ANC_SET_PARAM32 _IOWR(ANC_IOCTL_MAGIC, \
+ 300, compat_uptr_t)
+#define AUDIO_ANC_GET_PARAM32 _IOWR(ANC_IOCTL_MAGIC, \
+ 301, compat_uptr_t)
+
+static long audio_anc_compat_ioctl(struct file *f,
+ unsigned int cmd, unsigned long arg)
+{
+ unsigned int cmd64;
+ int ret = 0;
+
+ switch (cmd) {
+ case AUDIO_ANC_SET_PARAM32:
+ cmd64 = AUDIO_ANC_SET_PARAM;
+ break;
+ case AUDIO_ANC_GET_PARAM32:
+ cmd64 = AUDIO_ANC_GET_PARAM;
+ break;
+ default:
+ pr_err("%s: ioctl not found!\n", __func__);
+ ret = -EFAULT;
+ goto done;
+ }
+
+ ret = audio_anc_shared_ioctl(f, cmd64, compat_ptr(arg));
+done:
+ return ret;
+}
+#endif
+
+static const struct file_operations audio_anc_fops = {
+ .owner = THIS_MODULE,
+ .open = audio_anc_open,
+ .release = audio_anc_close,
+ .unlocked_ioctl = audio_anc_ioctl,
+#ifdef CONFIG_COMPAT
+ .compat_ioctl = audio_anc_compat_ioctl,
+#endif
+};
+
+int msm_anc_dev_create(struct platform_device *pdev)
+{
+ int result = 0;
+ dev_t dev = MKDEV(major, 0);
+ struct device *device_handle;
+
+ pr_debug("%s\n", __func__);
+
+ if (major) {
+ result = register_chrdev_region(dev, 1, DEVICE_NAME);
+ } else {
+ result = alloc_chrdev_region(&dev, 0, 1, DEVICE_NAME);
+ major = MAJOR(dev);
+ }
+
+ if (result < 0) {
+ pr_err("%s: Registering msm_audio_anc device failed\n",
+ __func__);
+ goto done;
+ }
+
+ audio_anc.anc_class = class_create(THIS_MODULE, "msm_audio_anc");
+ if (IS_ERR(audio_anc.anc_class)) {
+ result = PTR_ERR(audio_anc.anc_class);
+ pr_err("%s: Error creating anc class: %d\n",
+ __func__, result);
+ goto unregister_chrdev_region;
+ }
+
+ cdev_init(&audio_anc.myc, &audio_anc_fops);
+ result = cdev_add(&audio_anc.myc, dev, 1);
+
+ if (result < 0) {
+ pr_err("%s: Registering file operations failed\n",
+ __func__);
+ goto class_destroy;
+ }
+
+ device_handle = device_create(audio_anc.anc_class,
+ NULL, audio_anc.myc.dev, NULL, "msm_audio_anc");
+ if (IS_ERR(device_handle)) {
+ result = PTR_ERR(device_handle);
+ pr_err("%s: device_create failed: %d\n", __func__, result);
+ goto class_destroy;
+ }
+
+ pr_debug("exit %s\n", __func__);
+ return 0;
+
+class_destroy:
+ class_destroy(audio_anc.anc_class);
+unregister_chrdev_region:
+ unregister_chrdev_region(MKDEV(major, 0), 1);
+done:
+ pr_err("exit %s\n", __func__);
+ return result;
+}
+
+int msm_anc_dev_destroy(struct platform_device *pdev)
+{
+ device_destroy(audio_anc.anc_class, audio_anc.myc.dev);
+ cdev_del(&audio_anc.myc);
+ class_destroy(audio_anc.anc_class);
+ unregister_chrdev_region(MKDEV(major, 0), 1);
+
+ return 0;
+}
+
+static int __init audio_anc_init(void)
+{
+ return msm_anc_dev_init();
+}
+
+static void __exit audio_anc_exit(void)
+{
+ msm_anc_dev_deinit();
+}
+
+module_init(audio_anc_init);
+module_exit(audio_anc_exit);
+
+MODULE_DESCRIPTION("SoC QDSP6v2 Audio ANC driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/soc/qcom/qdsp6v2/sdsp-anc.c b/drivers/soc/qcom/qdsp6v2/sdsp-anc.c
new file mode 100644
index 000000000000..9294485f7ff2
--- /dev/null
+++ b/drivers/soc/qcom/qdsp6v2/sdsp-anc.c
@@ -0,0 +1,801 @@
+/* Copyright (c) 2018, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/slab.h>
+#include <linux/debugfs.h>
+#include <linux/kernel.h>
+#include <linux/kthread.h>
+#include <linux/uaccess.h>
+#include <linux/wait.h>
+#include <linux/wakelock.h>
+#include <linux/jiffies.h>
+#include <linux/sched.h>
+#include <linux/msm_audio_ion.h>
+#include <linux/delay.h>
+#include <sound/apr_audio-v2.h>
+#include <sound/q6afe-v2.h>
+#include <sound/q6audio-v2.h>
+#include <sound/audio_cal_utils.h>
+#include <sound/adsp_err.h>
+#include <linux/qdsp6v2/apr_tal.h>
+
+#include <linux/qdsp6v2/sdsp_anc.h>
+
+#define TIMEOUT_MS 1000
+
+struct anc_if_ctl {
+ void *apr;
+ atomic_t state;
+ atomic_t status;
+ wait_queue_head_t wait[AFE_MAX_PORTS];
+ struct task_struct *task;
+ struct anc_get_rpm_resp rpm_calib_data;
+ uint32_t mmap_handle;
+ struct mutex afe_cmd_lock;
+};
+
+static struct anc_if_ctl this_anc_if;
+
+static int32_t anc_get_param_callback(uint32_t *payload,
+ uint32_t payload_size)
+{
+ u32 param_id;
+ struct anc_get_rpm_resp *resp =
+ (struct anc_get_rpm_resp *) payload;
+
+ if (!(&(resp->pdata))) {
+ pr_err("%s: Error: resp pdata is NULL\n", __func__);
+ return -EINVAL;
+ }
+
+ param_id = resp->pdata.param_id;
+ if (param_id == AUD_MSVC_PARAM_ID_PORT_ANC_ALGO_RPM) {
+ if (payload_size < sizeof(this_anc_if.rpm_calib_data)) {
+ pr_err("%s: Error: received size %d, calib_data size %zu\n",
+ __func__, payload_size,
+ sizeof(this_anc_if.rpm_calib_data));
+ return -EINVAL;
+ }
+
+ memcpy(&this_anc_if.rpm_calib_data, payload,
+ sizeof(this_anc_if.rpm_calib_data));
+ if (!this_anc_if.rpm_calib_data.status) {
+ atomic_set(&this_anc_if.state, 0);
+ } else {
+ pr_debug("%s: calib resp status: %d", __func__,
+ this_anc_if.rpm_calib_data.status);
+ atomic_set(&this_anc_if.state, -1);
+ }
+ }
+
+ return 0;
+}
+
+static void anc_if_callback_debug_print(struct apr_client_data *data)
+{
+ uint32_t *payload;
+
+ payload = data->payload;
+
+ if (data->payload_size >= 8)
+ pr_debug("%s: code = 0x%x PL#0[0x%x], PL#1[0x%x], size = %d\n",
+ __func__, data->opcode, payload[0], payload[1],
+ data->payload_size);
+ else if (data->payload_size >= 4)
+ pr_debug("%s: code = 0x%x PL#0[0x%x], size = %d\n",
+ __func__, data->opcode, payload[0],
+ data->payload_size);
+ else
+ pr_debug("%s: code = 0x%x, size = %d\n",
+ __func__, data->opcode, data->payload_size);
+}
+
+static int32_t anc_if_callback(struct apr_client_data *data, void *priv)
+{
+ if (!data) {
+ pr_err("%s: Invalid param data\n", __func__);
+ return -EINVAL;
+ }
+ if (data->opcode == RESET_EVENTS) {
+ pr_debug("%s: reset event = %d %d apr[%pK]\n",
+ __func__,
+ data->reset_event, data->reset_proc, this_anc_if.apr);
+
+ if (this_anc_if.apr) {
+ apr_reset(this_anc_if.apr);
+ atomic_set(&this_anc_if.state, 0);
+ this_anc_if.apr = NULL;
+ }
+
+ return 0;
+ }
+ anc_if_callback_debug_print(data);
+ if (data->opcode == AFE_PORT_CMDRSP_GET_PARAM_V2) {
+ u8 *payload = data->payload;
+
+ if (!payload || (data->token >= AFE_MAX_PORTS)) {
+ pr_err("%s: Error: size %d payload %pK token %d\n",
+ __func__, data->payload_size,
+ payload, data->token);
+ return -EINVAL;
+ }
+
+ if (anc_get_param_callback(data->payload, data->payload_size))
+ return -EINVAL;
+
+ wake_up(&this_anc_if.wait[data->token]);
+
+ } else if (data->payload_size) {
+ uint32_t *payload;
+
+ payload = data->payload;
+ if (data->opcode == APR_BASIC_RSP_RESULT) {
+ pr_debug("%s:opcode = 0x%x cmd = 0x%x status = 0x%x token=%d\n",
+ __func__, data->opcode,
+ payload[0], payload[1], data->token);
+ /* payload[1] contains the error status for response */
+ if (payload[1] != 0) {
+ atomic_set(&this_anc_if.status, payload[1]);
+ pr_err("%s: cmd = 0x%x returned error = 0x%x\n",
+ __func__, payload[0], payload[1]);
+ }
+ switch (payload[0]) {
+ case AFE_PORT_CMD_SET_PARAM_V2:
+ case AFE_PORT_CMD_DEVICE_STOP:
+ case AFE_PORT_CMD_DEVICE_START:
+ case AFE_SERVICE_CMD_SHARED_MEM_MAP_REGIONS:
+ case AFE_SERVICE_CMD_SHARED_MEM_UNMAP_REGIONS:
+ case AFE_SVC_CMD_SET_PARAM:
+ atomic_set(&this_anc_if.state, 0);
+ wake_up(&this_anc_if.wait[data->token]);
+ break;
+ default:
+ pr_err("%s: Unknown cmd 0x%x\n", __func__,
+ payload[0]);
+ break;
+ }
+ } else if (data->opcode ==
+ AFE_SERVICE_CMDRSP_SHARED_MEM_MAP_REGIONS) {
+ pr_err("%s: ANC mmap_handle: 0x%x\n",
+ __func__, payload[0]);
+ this_anc_if.mmap_handle = payload[0];
+ atomic_set(&this_anc_if.state, 0);
+ wake_up(&this_anc_if.wait[data->token]);
+ }
+ }
+ return 0;
+}
+
+int anc_sdsp_interface_prepare(void)
+{
+ int ret = 0;
+
+ pr_debug("%s:\n", __func__);
+
+ if (this_anc_if.apr == NULL) {
+ this_anc_if.apr = apr_register("SDSP", "MAS", anc_if_callback,
+ 0xFFFFFFFF, &this_anc_if);
+ if (this_anc_if.apr == NULL) {
+ pr_err("%s: Unable to register AFE\n", __func__);
+ ret = -ENODEV;
+ }
+ }
+ return ret;
+}
+
+/*
+ * anc_if_apr_send_pkt : returns 0 on success, negative otherwise.
+ */
+static int anc_if_apr_send_pkt(void *data, wait_queue_head_t *wait)
+{
+ int ret;
+
+ if (wait)
+ atomic_set(&this_anc_if.state, 1);
+ atomic_set(&this_anc_if.status, 0);
+ ret = apr_send_pkt(this_anc_if.apr, data);
+ if (ret > 0) {
+ if (wait) {
+ ret = wait_event_timeout(*wait,
+ (atomic_read(&this_anc_if.state) == 0),
+ msecs_to_jiffies(TIMEOUT_MS));
+ if (!ret) {
+ ret = -ETIMEDOUT;
+ } else if (atomic_read(&this_anc_if.status) > 0) {
+ pr_err("%s: DSP returned error[%s]\n", __func__,
+ adsp_err_get_err_str(atomic_read(
+ &this_anc_if.status)));
+ ret = adsp_err_get_lnx_err_code(
+ atomic_read(&this_anc_if.status));
+ } else {
+ ret = 0;
+ }
+ } else {
+ ret = 0;
+ }
+ } else if (ret == 0) {
+ pr_err("%s: packet not transmitted\n", __func__);
+ /* apr_send_pkt can return 0 when nothing is transmitted */
+ ret = -EINVAL;
+ }
+
+ pr_debug("%s: leave %d\n", __func__, ret);
+ return ret;
+}
+
+static int anc_if_send_cmd_port_start(u16 port_id)
+{
+ struct afe_port_cmd_device_start start;
+ int ret, index;
+
+ pr_debug("%s: enter\n", __func__);
+ index = q6audio_get_port_index(port_id);
+ if (index < 0 || index > AFE_MAX_PORTS) {
+ pr_err("%s: AFE port index[%d] invalid!\n",
+ __func__, index);
+ return -EINVAL;
+ }
+ ret = q6audio_validate_port(port_id);
+ if (ret < 0) {
+ pr_err("%s: port id: 0x%x ret %d\n", __func__, port_id, ret);
+ return -EINVAL;
+ }
+
+ start.hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD,
+ APR_HDR_LEN(APR_HDR_SIZE),
+ APR_PKT_VER);
+ start.hdr.pkt_size = sizeof(start);
+ start.hdr.src_port = 0;
+ start.hdr.dest_port = 0;
+ start.hdr.token = index;
+ start.hdr.opcode = AFE_PORT_CMD_DEVICE_START;
+ start.port_id = q6audio_get_port_id(port_id);
+ pr_debug("%s: cmd device start opcode[0x%x] port id[0x%x]\n",
+ __func__, start.hdr.opcode, start.port_id);
+
+ ret = anc_if_apr_send_pkt(&start, &this_anc_if.wait[index]);
+ if (ret) {
+ pr_err("%s: AFE enable for port 0x%x failed %d\n", __func__,
+ port_id, ret);
+ } else if (this_anc_if.task != current) {
+ this_anc_if.task = current;
+ pr_debug("task_name = %s pid = %d\n",
+ this_anc_if.task->comm, this_anc_if.task->pid);
+ }
+
+ return ret;
+}
+
+int anc_if_send_cmd_port_stop(int port_id)
+{
+ struct afe_port_cmd_device_stop stop;
+ int ret = 0;
+
+ if (this_anc_if.apr == NULL) {
+ pr_err("%s: AFE is already closed\n", __func__);
+ ret = -EINVAL;
+ goto fail_cmd;
+ }
+ pr_debug("%s: port_id = 0x%x\n", __func__, port_id);
+ port_id = q6audio_convert_virtual_to_portid(port_id);
+
+ stop.hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD,
+ APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER);
+ stop.hdr.pkt_size = sizeof(stop);
+ stop.hdr.src_port = 0;
+ stop.hdr.dest_port = 0;
+ stop.hdr.token = 0;
+ stop.hdr.opcode = AFE_PORT_CMD_DEVICE_STOP;
+ stop.port_id = port_id;
+ stop.reserved = 0;
+
+ ret = anc_if_apr_send_pkt(&stop, NULL);
+ if (ret)
+ pr_err("%s: AFE close failed %d\n", __func__, ret);
+
+fail_cmd:
+ return ret;
+
+}
+
+int anc_if_config_ref(u16 port_id, u32 sample_rate,
+ u32 bit_width, u16 num_channel)
+{
+ struct anc_config_ref_command config;
+ int ret = 0;
+ int index;
+
+ ret = anc_sdsp_interface_prepare();
+ if (ret != 0) {
+ pr_err("%s: Q6 interface prepare failed %d\n", __func__, ret);
+ return ret;
+ }
+
+ index = q6audio_get_port_index(port_id);
+ memset(&config, 0, sizeof(config));
+ config.hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD,
+ APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER);
+ config.hdr.pkt_size = sizeof(config);
+ config.hdr.src_port = 0;
+ config.hdr.dest_port = 0;
+ config.hdr.token = index;
+ config.hdr.opcode = AFE_PORT_CMD_SET_PARAM_V2;
+ config.param.port_id = q6audio_get_port_id(port_id);
+ config.param.payload_size = sizeof(config) - sizeof(struct apr_hdr) -
+ sizeof(config.param);
+ config.param.payload_address_lsw = 0x00;
+ config.param.payload_address_msw = 0x00;
+ config.param.mem_map_handle = 0x00;
+ config.pdata.module_id = AUD_MSVC_MODULE_AUDIO_DEV_ANC_REFS;
+ config.pdata.param_id = AUD_MSVC_PARAM_ID_DEV_ANC_REFS_CONFIG;
+ config.pdata.param_size = sizeof(config.refs);
+ config.refs.minor_version = AUD_MSVC_API_VERSION_DEV_ANC_REFS_CONFIG;
+ config.refs.port_id = q6audio_get_port_id(port_id);
+ config.refs.sample_rate = sample_rate;
+ config.refs.bit_width = bit_width;
+ config.refs.num_channel = num_channel;
+
+ ret = anc_if_apr_send_pkt(&config, &this_anc_if.wait[index]);
+ if (ret) {
+ pr_err("%s: anc_if_config_ref for port 0x%x failed ret = %d\n",
+ __func__, port_id, ret);
+ pr_err("%s: anc_if_config_ref size of param is %lu\n",
+ __func__, sizeof(config.refs));
+ }
+
+ return ret;
+}
+
+int anc_if_share_resource(u16 port_id, u16 rddma_idx, u16 wrdma_idx,
+ u32 lpm_start_addr, u32 lpm_length)
+{
+ struct anc_share_resource_command config;
+ int ret = 0;
+ int index;
+
+ ret = anc_sdsp_interface_prepare();
+ if (ret != 0) {
+ pr_err("%s: Q6 interface prepare failed %d\n", __func__, ret);
+ return ret;
+ }
+
+ index = q6audio_get_port_index(port_id);
+ memset(&config, 0, sizeof(config));
+ config.hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD,
+ APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER);
+ config.hdr.pkt_size = sizeof(config);
+ config.hdr.src_port = 0;
+ config.hdr.dest_port = 0;
+ config.hdr.token = index;
+ config.hdr.opcode = AFE_PORT_CMD_SET_PARAM_V2;
+ config.param.port_id = q6audio_get_port_id(port_id);
+ config.param.payload_size = sizeof(config) - sizeof(struct apr_hdr) -
+ sizeof(config.param);
+ config.param.payload_address_lsw = 0x00;
+ config.param.payload_address_msw = 0x00;
+ config.param.mem_map_handle = 0x00;
+ config.pdata.module_id = AUD_MSVC_MODULE_AUDIO_DEV_RESOURCE_SHARE;
+ config.pdata.param_id = AUD_MSVC_PARAM_ID_PORT_SHARE_RESOURCE_CONFIG;
+ config.pdata.param_size = sizeof(config.resource);
+ config.resource.minor_version =
+ AUD_MSVC_API_VERSION_SHARE_RESOURCE_CONFIG;
+ config.resource.rddma_idx = rddma_idx;
+ config.resource.wrdma_idx = wrdma_idx;
+ config.resource.lpm_start_addr = lpm_start_addr;
+ config.resource.lpm_length = lpm_length;
+
+ ret = anc_if_apr_send_pkt(&config, &this_anc_if.wait[index]);
+ if (ret) {
+ pr_err("%s: share resource for port 0x%x failed ret = %d\n",
+ __func__, port_id, ret);
+ }
+
+ return ret;
+}
+
+int anc_if_tdm_port_start(u16 port_id, struct afe_tdm_port_config *tdm_port)
+{
+ struct aud_audioif_config_command config;
+ int ret = 0;
+ int index = 0;
+
+ if (!tdm_port) {
+ pr_err("%s: Error, no configuration data\n", __func__);
+ return -EINVAL;
+ }
+
+ pr_debug("%s: port id: 0x%x\n", __func__, port_id);
+
+ index = q6audio_get_port_index(port_id);
+ if (index < 0 || index > AFE_MAX_PORTS) {
+ pr_err("%s: AFE port index[%d] invalid!\n",
+ __func__, index);
+ return -EINVAL;
+ }
+
+ ret = anc_sdsp_interface_prepare();
+ if (ret != 0) {
+ pr_err("%s: Q6 interface prepare failed %d\n", __func__, ret);
+ return ret;
+ }
+
+ memset(&config, 0, sizeof(config));
+ config.hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD,
+ APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER);
+ config.hdr.pkt_size = sizeof(config);
+ config.hdr.src_port = 0;
+ config.hdr.dest_port = 0;
+ config.hdr.token = index;
+ config.hdr.opcode = AFE_PORT_CMD_SET_PARAM_V2;
+ config.param.port_id = q6audio_get_port_id(port_id);
+ config.param.payload_size = sizeof(config) - sizeof(struct apr_hdr) -
+ sizeof(config.param);
+ config.param.payload_address_lsw = 0x00;
+ config.param.payload_address_msw = 0x00;
+ config.param.mem_map_handle = 0x00;
+ config.pdata.module_id = AFE_MODULE_AUDIO_DEV_INTERFACE;
+ config.pdata.param_id = AFE_PARAM_ID_TDM_CONFIG;
+ config.pdata.param_size = sizeof(config.port);
+ config.port.tdm = tdm_port->tdm;
+
+ ret = anc_if_apr_send_pkt(&config, &this_anc_if.wait[index]);
+ if (ret) {
+ pr_err("%s: AFE enable for port 0x%x failed ret = %d\n",
+ __func__, port_id, ret);
+ goto fail_cmd;
+ }
+
+ ret = anc_if_send_cmd_port_start(port_id);
+
+fail_cmd:
+ return ret;
+}
+
+int anc_if_tdm_port_stop(u16 port_id)
+{
+ return anc_if_send_cmd_port_stop(port_id);
+}
+
+int anc_if_set_rpm(u16 port_id, u32 rpm)
+{
+ int ret = 0;
+ int index;
+
+ ret = anc_sdsp_interface_prepare();
+ if (ret != 0) {
+ pr_err("%s: Q6 interface prepare failed %d\n", __func__, ret);
+ return ret;
+ }
+
+ index = q6audio_get_port_index(port_id);
+
+ {
+ struct anc_set_rpm_command config;
+
+ memset(&config, 0, sizeof(config));
+ config.hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD,
+ APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER);
+ config.hdr.pkt_size = sizeof(config);
+ config.hdr.src_port = 0;
+ config.hdr.dest_port = 0;
+ config.hdr.token = index;
+ config.hdr.opcode = AFE_PORT_CMD_SET_PARAM_V2;
+ config.param.port_id = q6audio_get_port_id(port_id);
+ config.param.payload_size = sizeof(config) -
+ sizeof(struct apr_hdr) -
+ sizeof(config.param);
+ config.param.payload_address_lsw = 0x00;
+ config.param.payload_address_msw = 0x00;
+ config.param.mem_map_handle = 0x00;
+ config.pdata.module_id = AUD_MSVC_MODULE_AUDIO_DEV_ANC_ALGO;
+ config.pdata.param_id = AUD_MSVC_PARAM_ID_PORT_ANC_ALGO_RPM;
+ config.pdata.param_size = sizeof(config.set_rpm);
+ config.set_rpm.minor_version =
+ AUD_MSVC_API_VERSION_DEV_ANC_ALGO_RPM;
+ config.set_rpm.rpm = rpm;
+
+ ret = anc_if_apr_send_pkt(&config, &this_anc_if.wait[index]);
+ if (ret) {
+ pr_err("%s: share resource for port 0x%x failed ret = %d\n",
+ __func__, port_id, ret);
+ }
+ }
+
+ return ret;
+}
+
+int anc_if_set_bypass_mode(u16 port_id, u32 bypass_mode)
+{
+ int ret = 0;
+
+ int index;
+
+ ret = anc_sdsp_interface_prepare();
+ if (ret != 0) {
+ pr_err("%s: Q6 interface prepare failed %d\n", __func__, ret);
+ return ret;
+ }
+
+ index = q6audio_get_port_index(port_id);
+
+ {
+ struct anc_set_bypass_mode_command config;
+
+ memset(&config, 0, sizeof(config));
+ config.hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD,
+ APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER);
+ config.hdr.pkt_size = sizeof(config);
+ config.hdr.src_port = 0;
+ config.hdr.dest_port = 0;
+ config.hdr.token = index;
+ config.hdr.opcode = AFE_PORT_CMD_SET_PARAM_V2;
+ config.param.port_id = q6audio_get_port_id(port_id);
+ config.param.payload_size = sizeof(config) -
+ sizeof(struct apr_hdr) -
+ sizeof(config.param);
+ config.param.payload_address_lsw = 0x00;
+ config.param.payload_address_msw = 0x00;
+ config.param.mem_map_handle = 0x00;
+ config.pdata.module_id = AUD_MSVC_MODULE_AUDIO_DEV_ANC_ALGO;
+ config.pdata.param_id =
+ AUD_MSVC_PARAM_ID_PORT_ANC_ALGO_BYPASS_MODE;
+ config.pdata.param_size = sizeof(config.set_bypass_mode);
+ config.set_bypass_mode.minor_version =
+ AUD_MSVC_API_VERSION_DEV_ANC_ALGO_BYPASS_MODE;
+ config.set_bypass_mode.bypass_mode = bypass_mode;
+
+ ret = anc_if_apr_send_pkt(&config, &this_anc_if.wait[index]);
+ if (ret) {
+ pr_err("%s: share resource for port 0x%x failed ret = %d\n",
+ __func__, port_id, ret);
+ }
+ }
+
+ return ret;
+}
+
+int anc_if_set_algo_module_id(u16 port_id, u32 module_id)
+{
+ int ret = 0;
+
+ int index;
+
+ ret = anc_sdsp_interface_prepare();
+ if (ret != 0) {
+ pr_err("%s: Q6 interface prepare failed %d\n", __func__, ret);
+ return ret;
+ }
+
+ index = q6audio_get_port_index(port_id);
+
+ {
+ struct anc_set_algo_module_id_command config;
+
+ memset(&config, 0, sizeof(config));
+ config.hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD,
+ APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER);
+ config.hdr.pkt_size = sizeof(config);
+ config.hdr.src_port = 0;
+ config.hdr.dest_port = 0;
+ config.hdr.token = index;
+ config.hdr.opcode = AFE_PORT_CMD_SET_PARAM_V2;
+ config.param.port_id = q6audio_get_port_id(port_id);
+ config.param.payload_size = sizeof(config) -
+ sizeof(struct apr_hdr) -
+ sizeof(config.param);
+ config.param.payload_address_lsw = 0x00;
+ config.param.payload_address_msw = 0x00;
+ config.param.mem_map_handle = 0x00;
+ config.pdata.module_id = AUD_MSVC_MODULE_AUDIO_DEV_ANC_ALGO;
+ config.pdata.param_id =
+ AUD_MSVC_PARAM_ID_PORT_ANC_ALGO_MODULE_ID;
+ config.pdata.param_size = sizeof(config.set_algo_module_id);
+ config.set_algo_module_id.minor_version = 1;
+ config.set_algo_module_id.module_id = module_id;
+
+ ret = anc_if_apr_send_pkt(&config, &this_anc_if.wait[index]);
+ if (ret) {
+ pr_err("%s: anc algo module ID for port 0x%x failed ret = %d\n",
+ __func__, port_id, ret);
+ }
+ }
+
+ return ret;
+}
+
+int anc_if_set_anc_mic_spkr_layout(u16 port_id,
+struct aud_msvc_param_id_dev_anc_mic_spkr_layout_info *set_mic_spkr_layout_p)
+{
+ int ret = 0;
+
+ int index;
+
+ ret = anc_sdsp_interface_prepare();
+ if (ret != 0) {
+ pr_err("%s: Q6 interface prepare failed %d\n", __func__, ret);
+ return ret;
+ }
+
+ index = q6audio_get_port_index(port_id);
+
+ {
+ struct anc_set_mic_spkr_layout_info_command config;
+
+ memset(&config, 0, sizeof(config));
+ config.hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD,
+ APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER);
+ config.hdr.pkt_size = sizeof(config);
+ config.hdr.src_port = 0;
+ config.hdr.dest_port = 0;
+ config.hdr.token = index;
+ config.hdr.opcode = AFE_PORT_CMD_SET_PARAM_V2;
+ config.param.port_id = q6audio_get_port_id(port_id);
+ config.param.payload_size = sizeof(config) -
+ sizeof(struct apr_hdr) -
+ sizeof(config.param);
+ config.param.payload_address_lsw = 0x00;
+ config.param.payload_address_msw = 0x00;
+ config.param.mem_map_handle = 0x00;
+ config.pdata.module_id = AUD_MSVC_MODULE_AUDIO_DEV_ANC_ALGO;
+ config.pdata.param_id =
+ AUD_MSVC_PARAM_ID_PORT_ANC_MIC_SPKR_LAYOUT_INFO;
+ config.pdata.param_size = sizeof(config.set_mic_spkr_layout);
+
+ memcpy(&config.set_mic_spkr_layout, set_mic_spkr_layout_p,
+ sizeof(config.set_mic_spkr_layout));
+ ret = anc_if_apr_send_pkt(&config, &this_anc_if.wait[index]);
+ if (ret) {
+ pr_err("%s: anc algo module ID for port 0x%x failed ret = %d\n",
+ __func__, port_id, ret);
+ }
+ }
+
+ return ret;
+}
+
+int anc_if_cmd_memory_map(int port_id, phys_addr_t dma_addr_p,
+ u32 dma_buf_sz)
+{
+ int ret = 0;
+ int cmd_size = 0;
+ void *payload = NULL;
+ void *mmap_region_cmd = NULL;
+ struct afe_service_cmd_shared_mem_map_regions *mregion = NULL;
+ struct afe_service_shared_map_region_payload *mregion_pl = NULL;
+ int index = 0;
+
+ pr_debug("%s:\n", __func__);
+
+ ret = anc_sdsp_interface_prepare();
+ if (ret != 0) {
+ pr_err("%s: Q6 interface prepare failed %d\n", __func__, ret);
+ return ret;
+ }
+
+ index = q6audio_get_port_index(port_id);
+ if (index < 0 || index > AFE_MAX_PORTS) {
+ pr_err("%s: AFE port index[%d] invalid!\n",
+ __func__, index);
+ return -EINVAL;
+ }
+ ret = q6audio_validate_port(port_id);
+ if (ret < 0) {
+ pr_err("%s: Invalid port 0x%x ret %d",
+ __func__, port_id, ret);
+ return -EINVAL;
+ }
+
+ cmd_size = sizeof(struct afe_service_cmd_shared_mem_map_regions)
+ + sizeof(struct afe_service_shared_map_region_payload);
+
+ mmap_region_cmd = kzalloc(cmd_size, GFP_KERNEL);
+ if (!mmap_region_cmd) {
+ ret = -ENOMEM;
+ pr_err("%s: allocate mmap_region_cmd failed\n", __func__);
+ return ret;
+ }
+
+ mregion = (struct afe_service_cmd_shared_mem_map_regions *)
+ mmap_region_cmd;
+ mregion->hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD,
+ APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER);
+ mregion->hdr.pkt_size = cmd_size;
+ mregion->hdr.src_port = 0;
+ mregion->hdr.dest_port = 0;
+ mregion->hdr.token = index;
+ mregion->hdr.opcode = AFE_SERVICE_CMD_SHARED_MEM_MAP_REGIONS;
+ mregion->mem_pool_id = ADSP_MEMORY_MAP_SHMEM8_4K_POOL;
+ mregion->num_regions = 1;
+ mregion->property_flag = 0x00;
+
+ payload = ((u8 *) mmap_region_cmd +
+ sizeof(struct afe_service_cmd_shared_mem_map_regions));
+ mregion_pl = (struct afe_service_shared_map_region_payload *)payload;
+
+ mregion_pl->shm_addr_lsw = lower_32_bits(dma_addr_p);
+ mregion_pl->shm_addr_msw = msm_audio_populate_upper_32_bits(dma_addr_p);
+ mregion_pl->mem_size_bytes = dma_buf_sz;
+
+ ret = anc_if_apr_send_pkt(mmap_region_cmd, &this_anc_if.wait[index]);
+ if (ret)
+ pr_err("%s: AFE memory map cmd failed %d\n",
+ __func__, ret);
+ kfree(mmap_region_cmd);
+ return ret;
+}
+
+int anc_if_cmd_memory_unmap(int port_id, u32 mem_map_handle)
+{
+ int ret = 0;
+ struct afe_service_cmd_shared_mem_unmap_regions mregion;
+ int index = 0;
+
+ pr_debug("%s: handle 0x%x\n", __func__, mem_map_handle);
+
+ ret = anc_sdsp_interface_prepare();
+ if (ret != 0) {
+ pr_err("%s: Q6 interface prepare failed %d\n", __func__, ret);
+ return ret;
+ }
+
+ index = q6audio_get_port_index(port_id);
+ if (index < 0 || index > AFE_MAX_PORTS) {
+ pr_err("%s: AFE port index[%d] invalid!\n",
+ __func__, index);
+ return -EINVAL;
+ }
+ ret = q6audio_validate_port(port_id);
+ if (ret < 0) {
+ pr_err("%s: Invalid port 0x%x ret %d",
+ __func__, port_id, ret);
+ return -EINVAL;
+ }
+
+ mregion.hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD,
+ APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER);
+ mregion.hdr.pkt_size = sizeof(mregion);
+ mregion.hdr.src_port = 0;
+ mregion.hdr.dest_port = 0;
+ mregion.hdr.token = index;
+ mregion.hdr.opcode = AFE_SERVICE_CMD_SHARED_MEM_UNMAP_REGIONS;
+ mregion.mem_map_handle = mem_map_handle;
+
+ ret = anc_if_apr_send_pkt(&mregion, &this_anc_if.wait[index]);
+ if (ret)
+ pr_err("%s: msvc memory unmap cmd failed %d\n",
+ __func__, ret);
+
+ return ret;
+}
+
+static int __init sdsp_anc_init(void)
+{
+ int i = 0, ret = 0;
+
+ atomic_set(&this_anc_if.state, 0);
+ atomic_set(&this_anc_if.status, 0);
+ this_anc_if.apr = NULL;
+ this_anc_if.mmap_handle = 0;
+ mutex_init(&this_anc_if.afe_cmd_lock);
+ for (i = 0; i < AFE_MAX_PORTS; i++)
+ init_waitqueue_head(&this_anc_if.wait[i]);
+
+ return ret;
+}
+
+static void __exit sdsp_anc_exit(void)
+{
+ mutex_destroy(&this_anc_if.afe_cmd_lock);
+}
+
+device_initcall(sdsp_anc_init);
+__exitcall(sdsp_anc_exit);
diff --git a/include/linux/qdsp6v2/audio-anc-dev-mgr.h b/include/linux/qdsp6v2/audio-anc-dev-mgr.h
new file mode 100644
index 000000000000..dfa6752bc31b
--- /dev/null
+++ b/include/linux/qdsp6v2/audio-anc-dev-mgr.h
@@ -0,0 +1,46 @@
+/* Copyright (c) 2018, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef _AUDIO_ANC_DEV_MGR_H_
+#define _AUDIO_ANC_DEV_MGR_H_
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/device.h>
+#include <linux/platform_device.h>
+#include <linux/mfd/wcd9xxx/core.h>
+#include <linux/bitops.h>
+#include <linux/slab.h>
+#include <linux/clk.h>
+#include <linux/of_device.h>
+#include <linux/clk/msm-clk.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/soc.h>
+#include <sound/apr_audio-v2.h>
+#include <sound/q6afe-v2.h>
+#include <sound/msm-dai-q6-v2.h>
+#include <linux/msm_audio_anc.h>
+
+int msm_anc_dev_init(void);
+int msm_anc_dev_deinit(void);
+
+int msm_anc_dev_start(void);
+int msm_anc_dev_stop(void);
+
+int msm_anc_dev_set_info(void *info_p, int32_t anc_cmd);
+
+int msm_anc_dev_create(struct platform_device *pdev);
+
+int msm_anc_dev_destroy(struct platform_device *pdev);
+
+#endif
diff --git a/include/linux/qdsp6v2/sdsp_anc.h b/include/linux/qdsp6v2/sdsp_anc.h
new file mode 100644
index 000000000000..3b236e827e3d
--- /dev/null
+++ b/include/linux/qdsp6v2/sdsp_anc.h
@@ -0,0 +1,302 @@
+/* Copyright (c) 2018, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+#ifndef __SDSP_ANC_H__
+#define __SDSP_ANC_H__
+
+#include <sound/q6afe-v2.h>
+#include <sound/apr_audio-v2.h>
+
+
+#define AUD_MSVC_MODULE_AUDIO_DEV_RESOURCE_SHARE 0x0001028A
+#define AUD_MSVC_PARAM_ID_PORT_SHARE_RESOURCE_CONFIG 0x00010297
+#define AUD_MSVC_API_VERSION_SHARE_RESOURCE_CONFIG 0x1
+#define AUD_MSVC_MODULE_AUDIO_DEV_ANC_REFS 0x00010254
+#define AUD_MSVC_PARAM_ID_DEV_ANC_REFS_CONFIG 0x00010286
+#define AUD_MSVC_API_VERSION_DEV_ANC_REFS_CONFIG 0x1
+#define AUD_MSVC_MODULE_AUDIO_DEV_ANC_ALGO 0x00010234
+#define AUD_MSVC_PARAM_ID_PORT_ANC_ALGO_RPM 0x00010235
+#define AUD_MSVC_API_VERSION_DEV_ANC_ALGO_RPM 0x1
+
+struct aud_msvc_port_param_data_v2 {
+ /* ID of the module to be configured.
+ * Supported values: Valid module ID
+ */
+ u32 module_id;
+
+ /* ID of the parameter corresponding to the supported parameters
+ * for the module ID.
+ * Supported values: Valid parameter ID
+ */
+ u32 param_id;
+
+ /* Actual size of the data for the
+ * module_id/param_id pair. The size is a
+ * multiple of four bytes.
+ * Supported values: > 0
+ */
+ u16 param_size;
+
+ /* This field must be set to zero.
+ */
+ u16 reserved;
+} __packed;
+
+
+/* Payload of the #AFE_PORT_CMD_SET_PARAM_V2 command's
+ * configuration/calibration settings for the AFE port.
+ */
+struct aud_msvc_port_cmd_set_param_v2 {
+ /* Port interface and direction (Rx or Tx) to start.
+ */
+ u16 port_id;
+
+ /* Actual size of the payload in bytes.
+ * This is used for parsing the parameter payload.
+ * Supported values: > 0
+ */
+ u16 payload_size;
+
+ /* LSW of 64 bit Payload address.
+ * Address should be 32-byte,
+ * 4kbyte aligned and must be contiguous memory.
+ */
+ u32 payload_address_lsw;
+
+ /* MSW of 64 bit Payload address.
+ * In case of 32-bit shared memory address,
+ * this field must be set to zero.
+ * In case of 36-bit shared memory address,
+ * bit-4 to bit-31 must be set to zero.
+ * Address should be 32-byte, 4kbyte aligned
+ * and must be contiguous memory.
+ */
+ u32 payload_address_msw;
+
+ /* Memory map handle returned by
+ * AFE_SERVICE_CMD_SHARED_MEM_MAP_REGIONS commands.
+ * Supported Values:
+ * - NULL -- Message. The parameter data is in-band.
+ * - Non-NULL -- The parameter data is Out-band.Pointer to
+ * the physical address
+ * in shared memory of the payload data.
+ * An optional field is available if parameter
+ * data is in-band:
+ * aud_msvc_param_data_v2 param_data[...].
+ * For detailed payload content, see the
+ * aud_msvc_port_param_data_v2 structure.
+ */
+ u32 mem_map_handle;
+
+} __packed;
+
+/* Payload of the #AFE_PORT_CMD_GET_PARAM_V2 command,
+ * which queries for one post/preprocessing parameter of a
+ * stream.
+ */
+struct aud_msvc_port_cmd_get_param_v2 {
+ /* Port interface and direction (Rx or Tx) to start. */
+ u16 port_id;
+
+ /* Maximum data size of the parameter ID/module ID combination.
+ * This is a multiple of four bytes
+ * Supported values: > 0
+ */
+ u16 payload_size;
+
+ /* LSW of 64 bit Payload address. Address should be 32-byte,
+ * 4kbyte aligned and must be contig memory.
+ */
+ u32 payload_address_lsw;
+
+ /* MSW of 64 bit Payload address. In case of 32-bit shared
+ * memory address, this field must be set to zero. In case of 36-bit
+ * shared memory address, bit-4 to bit-31 must be set to zero.
+ * Address should be 32-byte, 4kbyte aligned and must be contiguous
+ * memory.
+ */
+ u32 payload_address_msw;
+
+ /* Memory map handle returned by
+ * AFE_SERVICE_CMD_SHARED_MEM_MAP_REGIONS commands.
+ * Supported Values: - NULL -- Message. The parameter data is
+ * in-band. - Non-NULL -- The parameter data is Out-band.Pointer to
+ * - the physical address in shared memory of the payload data.
+ * For detailed payload content, see the aud_msvc_port_param_data_v2
+ * structure
+ */
+ u32 mem_map_handle;
+
+ /* ID of the module to be queried.
+ * Supported values: Valid module ID
+ */
+ u32 module_id;
+
+ /* ID of the parameter to be queried.
+ * Supported values: Valid parameter ID
+ */
+ u32 param_id;
+
+} __packed;
+
+struct aud_audioif_config_command {
+ struct apr_hdr hdr;
+ struct aud_msvc_port_cmd_set_param_v2 param;
+ struct aud_msvc_port_param_data_v2 pdata;
+ union afe_port_config port;
+} __packed;
+
+struct aud_msvc_param_id_dev_share_resource_cfg {
+ u32 minor_version;
+ u16 rddma_idx;
+ u16 wrdma_idx;
+ u32 lpm_start_addr;
+ u32 lpm_length;
+} __packed;
+
+
+struct aud_msvc_param_id_dev_anc_algo_rpm {
+ u32 minor_version;
+ u32 rpm;
+} __packed;
+
+
+struct aud_msvc_param_id_dev_anc_refs_cfg {
+ u32 minor_version;
+ u16 port_id;
+ u16 num_channel;
+ u32 sample_rate;
+ u32 bit_width;
+} __packed;
+
+
+struct anc_share_resource_command {
+ struct apr_hdr hdr;
+ struct aud_msvc_port_cmd_set_param_v2 param;
+ struct aud_msvc_port_param_data_v2 pdata;
+ struct aud_msvc_param_id_dev_share_resource_cfg resource;
+} __packed;
+
+
+struct anc_config_ref_command {
+ struct apr_hdr hdr;
+ struct aud_msvc_port_cmd_set_param_v2 param;
+ struct aud_msvc_port_param_data_v2 pdata;
+ struct aud_msvc_param_id_dev_anc_refs_cfg refs;
+} __packed;
+
+
+
+struct anc_set_rpm_command {
+ struct apr_hdr hdr;
+ struct aud_msvc_port_cmd_set_param_v2 param;
+ struct aud_msvc_port_param_data_v2 pdata;
+ struct aud_msvc_param_id_dev_anc_algo_rpm set_rpm;
+} __packed;
+
+struct anc_get_rpm_command {
+ struct apr_hdr hdr;
+ struct aud_msvc_port_cmd_get_param_v2 param;
+ struct aud_msvc_port_param_data_v2 pdata;
+ struct aud_msvc_param_id_dev_anc_algo_rpm get_rpm;
+} __packed;
+
+struct anc_get_rpm_resp {
+ uint32_t status;
+ struct aud_msvc_port_param_data_v2 pdata;
+ struct aud_msvc_param_id_dev_anc_algo_rpm res_rpm;
+} __packed;
+
+#define AUD_MSVC_PARAM_ID_PORT_ANC_ALGO_BYPASS_MODE 0x0001029B
+
+#define AUD_MSVC_API_VERSION_DEV_ANC_ALGO_BYPASS_MODE 0x1
+
+#define AUD_MSVC_ANC_ALGO_BYPASS_MODE_NO 0x0
+#define AUD_MSVC_ANC_ALGO_BYPASS_MODE_REFS_TO_ANC_SPKR 0x1
+#define AUD_MSVC_ANC_ALGO_BYPASS_MODE_ANC_MIC_TO_ANC_SPKR 0x2
+#define AUD_MSVC_ANC_ALGO_BYPASS_MODE_REFS_MIXED_ANC_MIC_TO_ANC_SPKR 0x3
+
+struct aud_msvc_param_id_dev_anc_algo_bypass_mode {
+ uint32_t minor_version;
+ uint32_t bypass_mode;
+} __packed;
+
+struct anc_set_bypass_mode_command {
+ struct apr_hdr hdr;
+ struct aud_msvc_port_cmd_set_param_v2 param;
+ struct aud_msvc_port_param_data_v2 pdata;
+ struct aud_msvc_param_id_dev_anc_algo_bypass_mode set_bypass_mode;
+} __packed;
+
+#define AUD_MSVC_PARAM_ID_PORT_ANC_ALGO_MODULE_ID 0x0001023A
+
+struct aud_msvc_param_id_dev_anc_algo_module_id {
+ uint32_t minor_version;
+ uint32_t module_id;
+} __packed;
+
+struct anc_set_algo_module_id_command {
+ struct apr_hdr hdr;
+ struct aud_msvc_port_cmd_set_param_v2 param;
+ struct aud_msvc_port_param_data_v2 pdata;
+ struct aud_msvc_param_id_dev_anc_algo_module_id set_algo_module_id;
+} __packed;
+
+
+#define AUD_MSVC_PARAM_ID_PORT_ANC_MIC_SPKR_LAYOUT_INFO 0x0001029C
+
+#define AUD_MSVC_API_VERSION_DEV_ANC_MIC_SPKR_LAYOUT_INFO 0x1
+
+#define AUD_MSVC_ANC_MAX_NUM_OF_MICS 16
+#define AUD_MSVC_ANC_MAX_NUM_OF_SPKRS 16
+
+struct aud_msvc_param_id_dev_anc_mic_spkr_layout_info {
+ uint32_t minor_version;
+ uint16_t mic_layout_array[AUD_MSVC_ANC_MAX_NUM_OF_MICS];
+ uint16_t spkr_layout_array[AUD_MSVC_ANC_MAX_NUM_OF_SPKRS];
+ uint16_t num_anc_mic;
+ uint16_t num_anc_spkr;
+ uint16_t num_add_mic_signal;
+ uint16_t num_add_spkr_signal;
+} __packed;
+
+struct anc_set_mic_spkr_layout_info_command {
+ struct apr_hdr hdr;
+ struct aud_msvc_port_cmd_set_param_v2 param;
+ struct aud_msvc_port_param_data_v2 pdata;
+ struct aud_msvc_param_id_dev_anc_mic_spkr_layout_info
+ set_mic_spkr_layout;
+} __packed;
+
+int anc_if_tdm_port_start(u16 port_id, struct afe_tdm_port_config *tdm_port);
+
+int anc_if_tdm_port_stop(u16 port_id);
+
+int anc_if_share_resource(u16 port_id, u16 rddma_idx, u16 wrdma_idx,
+ u32 lpm_start_addr, u32 lpm_length);
+
+int anc_if_config_ref(u16 port_id, u32 sample_rate, u32 bit_width,
+ u16 num_channel);
+
+int anc_if_set_rpm(u16 port_id, u32 rpm);
+
+int anc_if_set_bypass_mode(u16 port_id, u32 bypass_mode);
+
+int anc_if_set_algo_module_id(u16 port_id, u32 module_id);
+
+int anc_if_set_anc_mic_spkr_layout(u16 port_id,
+struct aud_msvc_param_id_dev_anc_mic_spkr_layout_info *set_mic_spkr_layout_p);
+
+int anc_if_shared_mem_map(void);
+
+int anc_if_shared_mem_unmap(void);
+
+#endif /* __SDSP_ANC_H__ */
diff --git a/include/uapi/linux/Kbuild b/include/uapi/linux/Kbuild
index 2604d3f387ba..c06237170542 100644
--- a/include/uapi/linux/Kbuild
+++ b/include/uapi/linux/Kbuild
@@ -296,6 +296,7 @@ header-y += msm_audio_amrnb.h
header-y += msm_audio_amrwb.h
header-y += msm_audio_amrwbplus.h
header-y += msm_audio_calibration.h
+header-y += msm_audio_anc.h
header-y += msm_audio_mvs.h
header-y += msm_audio_qcp.h
header-y += msm_audio_sbc.h
diff --git a/include/uapi/linux/msm_audio_anc.h b/include/uapi/linux/msm_audio_anc.h
new file mode 100644
index 000000000000..028d381bc1a6
--- /dev/null
+++ b/include/uapi/linux/msm_audio_anc.h
@@ -0,0 +1,53 @@
+#ifndef _UAPI_MSM_AUDIO_ANC_H
+#define _UAPI_MSM_AUDIO_ANC_H
+
+#include <linux/types.h>
+#include <linux/ioctl.h>
+
+#define ANC_IOCTL_MAGIC 'a'
+
+#define AUDIO_ANC_SET_PARAM _IOWR(ANC_IOCTL_MAGIC, \
+ 300, struct audio_anc_packet *)
+#define AUDIO_ANC_GET_PARAM _IOWR(ANC_IOCTL_MAGIC, \
+ 301, struct audio_anc_packet *)
+
+#define ANC_CMD_START 0
+#define ANC_CMD_STOP 1
+#define ANC_CMD_RPM 2
+#define ANC_CMD_BYPASS_MODE 3
+#define ANC_CMD_ALGO_MODULE 4
+
+/* room for ANC_CMD define extend */
+#define ANC_CMD_MAX 0xFF
+
+struct audio_anc_header {
+ int32_t data_size;
+ int32_t version;
+ int32_t anc_cmd;
+ int32_t anc_cmd_size;
+};
+
+struct audio_anc_rpm_info {
+ int32_t rpm;
+};
+
+struct audio_anc_bypass_mode {
+ int32_t mode;
+};
+
+struct audio_anc_algo_module_info {
+ int32_t module_id;
+};
+
+union audio_anc_data {
+ struct audio_anc_rpm_info rpm_info;
+ struct audio_anc_bypass_mode bypass_mode_info;
+ struct audio_anc_algo_module_info algo_info;
+};
+
+struct audio_anc_packet {
+ struct audio_anc_header hdr;
+ union audio_anc_data anc_data;
+};
+
+#endif /* _UAPI_MSM_AUDIO_ANC_H */