diff options
-rw-r--r-- | Documentation/devicetree/bindings/sound/qcom-audio-dev.txt | 66 | ||||
-rw-r--r-- | arch/arm/boot/dts/qcom/apq8096-v3-auto-adp.dts | 6 | ||||
-rw-r--r-- | arch/arm/boot/dts/qcom/apq8096pro-v1.1-auto-adp-lite.dts | 4 | ||||
-rw-r--r-- | arch/arm/boot/dts/qcom/apq8096pro-v1.1-auto-adp.dts | 4 | ||||
-rw-r--r-- | arch/arm/boot/dts/qcom/msm8996-agave-adp.dtsi | 37 | ||||
-rw-r--r-- | arch/arm/boot/dts/qcom/msm8996-v3-auto-adp.dts | 6 | ||||
-rw-r--r-- | arch/arm/boot/dts/qcom/msm8996pro-auto-adp-lite.dts | 4 | ||||
-rw-r--r-- | arch/arm/boot/dts/qcom/msm8996pro-auto-adp.dts | 4 | ||||
-rw-r--r-- | drivers/soc/qcom/Kconfig | 7 | ||||
-rw-r--r-- | drivers/soc/qcom/qdsp6v2/Makefile | 2 | ||||
-rw-r--r-- | drivers/soc/qcom/qdsp6v2/audio-anc-dev-mgr.c | 1170 | ||||
-rw-r--r-- | drivers/soc/qcom/qdsp6v2/audio_anc.c | 350 | ||||
-rw-r--r-- | drivers/soc/qcom/qdsp6v2/sdsp-anc.c | 801 | ||||
-rw-r--r-- | include/linux/qdsp6v2/audio-anc-dev-mgr.h | 46 | ||||
-rw-r--r-- | include/linux/qdsp6v2/sdsp_anc.h | 302 | ||||
-rw-r--r-- | include/uapi/linux/Kbuild | 1 | ||||
-rw-r--r-- | include/uapi/linux/msm_audio_anc.h | 53 |
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 */ |