diff options
46 files changed, 3767 insertions, 101 deletions
diff --git a/Android.bp b/Android.bp new file mode 100644 index 000000000000..4341e3a71dad --- /dev/null +++ b/Android.bp @@ -0,0 +1,27 @@ +cc_binary_host { + name: "unifdef", + srcs: ["scripts/unifdef.c"], + sanitize: { + never: true, + } +} + +gensrcs { + name: "qseecom-kernel-includes", + + // move to out/ as root for header generation because of scripts/unifdef + // storage - at the expense of extra ../ references + cmd: "pushd out && mkdir -p scripts && rm -f scripts/unifdef && ln -s ../../$(location unifdef) scripts/unifdef && ../$(location scripts/headers_install.sh) `dirname ../$(out)` ../ $(in) && popd", + + tools: ["unifdef"], + tool_files: ["scripts/headers_install.sh"], + export_include_dirs: ["include/uapi"], + srcs: ["include/uapi/linux/qseecom.h"], + output_extension: "h", +} + +cc_library_headers { + name: "qseecom-kernel-headers", + generated_headers: ["qseecom-kernel-includes"], + export_generated_headers: ["qseecom-kernel-includes"], +} 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..2ab2d00de34b 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>; @@ -1547,3 +1584,17 @@ }; }; }; + +/ { + qcom,sde-reserved-plane { + qcom,sde-plane-id@0 { + reg = <0x0>; + qcom,plane-name = "vig0"; + }; + }; +}; + +&sde_kms { + contiguous-region = <&cont_splash_mem &cont_splash_mem_hdmi + &early_camera_mem>; +}; diff --git a/arch/arm/boot/dts/qcom/msm8996-auto-cdp.dtsi b/arch/arm/boot/dts/qcom/msm8996-auto-cdp.dtsi index 7cbc09767f62..2702ddb23a43 100644 --- a/arch/arm/boot/dts/qcom/msm8996-auto-cdp.dtsi +++ b/arch/arm/boot/dts/qcom/msm8996-auto-cdp.dtsi @@ -1404,3 +1404,16 @@ }; }; +/ { + qcom,sde-reserved-plane { + qcom,sde-plane-id@0 { + reg = <0x0>; + qcom,plane-name = "vig0"; + }; + }; +}; + +&sde_kms { + contiguous-region = <&cont_splash_mem &cont_splash_mem_hdmi + &early_camera_mem>; +}; 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/arch/arm/boot/dts/qcom/vplatform-lfv-msm8996-ivi-la.dts b/arch/arm/boot/dts/qcom/vplatform-lfv-msm8996-ivi-la.dts index 7900d963bef3..21e358afd21a 100644 --- a/arch/arm/boot/dts/qcom/vplatform-lfv-msm8996-ivi-la.dts +++ b/arch/arm/boot/dts/qcom/vplatform-lfv-msm8996-ivi-la.dts @@ -46,6 +46,11 @@ }; }; }; + + bluetooth: bt_qca6174 { + compatible = "qca,qca6174"; + qca,bt-reset-gpio = <&pm8994_gpios 19 0>; /* BT_EN */ + }; }; &soc { diff --git a/arch/arm64/configs/msm-auto-gvm-perf_defconfig b/arch/arm64/configs/msm-auto-gvm-perf_defconfig index 91115071f99b..55655ac06803 100644 --- a/arch/arm64/configs/msm-auto-gvm-perf_defconfig +++ b/arch/arm64/configs/msm-auto-gvm-perf_defconfig @@ -164,8 +164,13 @@ CONFIG_RMNET_DATA_FC=y CONFIG_RMNET_DATA_DEBUG_PKT=y CONFIG_CAN=y CONFIG_CAN_RH850=y +CONFIG_BT=y +CONFIG_MSM_BT_POWER=y +CONFIG_BTFM_SLIM=y +CONFIG_BTFM_SLIM_WCN3990=y CONFIG_CFG80211=y CONFIG_CFG80211_INTERNAL_REGDB=y +CONFIG_RFKILL=y CONFIG_IPC_ROUTER=y CONFIG_IPC_ROUTER_SECURITY=y CONFIG_DEVTMPFS=y @@ -223,6 +228,8 @@ CONFIG_DIAG_CHAR=y CONFIG_MSM_SMD_PKT=y CONFIG_I2C_CHARDEV=y CONFIG_I2C_MSM_V2=y +CONFIG_SLIMBUS=y +CONFIG_SLIMBUS_MSM_NGD=y CONFIG_SPI=y CONFIG_SPI_QUP=y CONFIG_SPI_SPIDEV=y diff --git a/arch/arm64/configs/msm-auto-gvm_defconfig b/arch/arm64/configs/msm-auto-gvm_defconfig index 56f6629ee4fb..177ccef17f77 100644 --- a/arch/arm64/configs/msm-auto-gvm_defconfig +++ b/arch/arm64/configs/msm-auto-gvm_defconfig @@ -163,8 +163,13 @@ CONFIG_RMNET_DATA_FC=y CONFIG_RMNET_DATA_DEBUG_PKT=y CONFIG_CAN=y CONFIG_CAN_RH850=y +CONFIG_BT=y +CONFIG_MSM_BT_POWER=y +CONFIG_BTFM_SLIM=y +CONFIG_BTFM_SLIM_WCN3990=y CONFIG_CFG80211=y CONFIG_CFG80211_INTERNAL_REGDB=y +CONFIG_RFKILL=y CONFIG_IPC_ROUTER=y CONFIG_IPC_ROUTER_SECURITY=y CONFIG_DEVTMPFS=y @@ -225,6 +230,8 @@ CONFIG_DIAG_CHAR=y CONFIG_MSM_SMD_PKT=y CONFIG_I2C_CHARDEV=y CONFIG_I2C_MSM_V2=y +CONFIG_SLIMBUS=y +CONFIG_SLIMBUS_MSM_NGD=y CONFIG_SPI=y CONFIG_SPI_DEBUG=y CONFIG_SPI_QUP=y diff --git a/drivers/input/touchscreen/synaptics_dsx/synaptics_dsx_fw_update.c b/drivers/input/touchscreen/synaptics_dsx/synaptics_dsx_fw_update.c index 4787f2bcd768..13680130c2de 100644 --- a/drivers/input/touchscreen/synaptics_dsx/synaptics_dsx_fw_update.c +++ b/drivers/input/touchscreen/synaptics_dsx/synaptics_dsx_fw_update.c @@ -681,10 +681,12 @@ static enum flash_area fwu_go_nogo(struct image_header_data *header) goto exit; } - while (strptr[index] >= '0' && strptr[index] <= '9') { + while ((index < MAX_FIRMWARE_ID_LEN - 1) && strptr[index] >= '0' + && strptr[index] <= '9') { firmware_id[index] = strptr[index]; index++; } + firmware_id[index] = '\0'; retval = sstrtoul(firmware_id, 10, &image_fw_id); kfree(firmware_id); diff --git a/drivers/irqchip/irq-gic.c b/drivers/irqchip/irq-gic.c index 10b73d9bea78..b0b534622734 100644 --- a/drivers/irqchip/irq-gic.c +++ b/drivers/irqchip/irq-gic.c @@ -41,6 +41,7 @@ #include <linux/irqchip.h> #include <linux/irqchip/chained_irq.h> #include <linux/irqchip/arm-gic.h> +#include <linux/syscore_ops.h> #include <asm/cputype.h> #include <asm/irq.h> @@ -69,6 +70,7 @@ union gic_base { }; struct gic_chip_data { + unsigned int irq_offset; union gic_base dist_base; union gic_base cpu_base; #ifdef CONFIG_CPU_PM @@ -85,6 +87,10 @@ struct gic_chip_data { #ifdef CONFIG_GIC_NON_BANKED void __iomem *(*get_base)(union gic_base *); #endif +#ifdef CONFIG_PM + unsigned int wakeup_irqs[32]; + unsigned int enabled_irqs[32]; +#endif }; static DEFINE_RAW_SPINLOCK(irq_controller_lock); @@ -222,6 +228,109 @@ static void gic_unmask_irq(struct irq_data *d) raw_spin_unlock_irqrestore(&irq_controller_lock, flags); } +#ifdef CONFIG_PM +static int gic_suspend_one(struct gic_chip_data *gic) +{ + unsigned int i; + void __iomem *base = gic_data_dist_base(gic); + + for (i = 0; i * 32 < gic->gic_irqs; i++) { + gic->enabled_irqs[i] + = readl_relaxed(base + GIC_DIST_ENABLE_SET + i * 4); + /* disable all of them */ + writel_relaxed(0xffffffff, + base + GIC_DIST_ENABLE_CLEAR + i * 4); + /* enable the wakeup set */ + writel_relaxed(gic->wakeup_irqs[i], + base + GIC_DIST_ENABLE_SET + i * 4); + } + /* make sure all gic setting finished */ + mb(); + return 0; +} + +static int gic_suspend(void) +{ + int i; + + for (i = 0; i < MAX_GIC_NR; i++) + gic_suspend_one(&gic_data[i]); + return 0; +} + +static void gic_show_resume_irq(struct gic_chip_data *gic) +{ + unsigned int i; + u32 enabled; + u32 pending[32]; + void __iomem *base = gic_data_dist_base(gic); + + raw_spin_lock(&irq_controller_lock); + for (i = 0; i * 32 < gic->gic_irqs; i++) { + enabled = readl_relaxed(base + GIC_DIST_ENABLE_CLEAR + i * 4); + pending[i] = readl_relaxed(base + GIC_DIST_PENDING_SET + i * 4); + pending[i] &= enabled; + } + raw_spin_unlock(&irq_controller_lock); + + for (i = find_first_bit((unsigned long *)pending, gic->gic_irqs); + i < gic->gic_irqs; + i = find_next_bit((unsigned long *)pending, + gic->gic_irqs, i+1)) { + unsigned int irq = irq_find_mapping(gic->domain, + i + gic->irq_offset); + struct irq_desc *desc = irq_to_desc(irq); + const char *name = "null"; + + if (desc == NULL) + name = "stray irq"; + else if (desc->action && desc->action->name) + name = desc->action->name; + + pr_warn("%s: %d triggered %s\n", __func__, + i + gic->irq_offset, name); + } +} + +static void gic_resume_one(struct gic_chip_data *gic) +{ + unsigned int i; + void __iomem *base = gic_data_dist_base(gic); + + gic_show_resume_irq(gic); + for (i = 0; i * 32 < gic->gic_irqs; i++) { + /* disable all of them */ + writel_relaxed(0xffffffff, + base + GIC_DIST_ENABLE_CLEAR + i * 4); + /* enable the enabled set */ + writel_relaxed(gic->enabled_irqs[i], + base + GIC_DIST_ENABLE_SET + i * 4); + } + /* make sure all gic setting finished */ + mb(); +} + +static void gic_resume(void) +{ + int i; + + for (i = 0; i < MAX_GIC_NR; i++) + gic_resume_one(&gic_data[i]); +} + +static struct syscore_ops gic_syscore_ops = { + .suspend = gic_suspend, + .resume = gic_resume, +}; + +static int __init gic_init_sys(void) +{ + register_syscore_ops(&gic_syscore_ops); + return 0; +} +arch_initcall(gic_init_sys); +#endif + static void gic_eoi_irq(struct irq_data *d) { if (gic_arch_extn.irq_eoi) { @@ -373,6 +482,20 @@ static int gic_set_affinity(struct irq_data *d, const struct cpumask *mask_val, static int gic_set_wake(struct irq_data *d, unsigned int on) { int ret = -ENXIO; + unsigned int reg_offset, bit_offset; + unsigned int gicirq = gic_irq(d); + struct gic_chip_data *gic_data = irq_data_get_irq_chip_data(d); + + /* per-cpu interrupts cannot be wakeup interrupts */ + WARN_ON(gicirq < 32); + + reg_offset = gicirq / 32; + bit_offset = gicirq % 32; + + if (on) + gic_data->wakeup_irqs[reg_offset] |= 1 << bit_offset; + else + gic_data->wakeup_irqs[reg_offset] &= ~(1 << bit_offset); if (gic_arch_extn.irq_set_wake) ret = gic_arch_extn.irq_set_wake(d, on); diff --git a/drivers/power/supply/qcom/qpnp-smb2.c b/drivers/power/supply/qcom/qpnp-smb2.c index 4beaddff47b3..02b1204789bf 100644 --- a/drivers/power/supply/qcom/qpnp-smb2.c +++ b/drivers/power/supply/qcom/qpnp-smb2.c @@ -1829,7 +1829,8 @@ static int smb2_chg_config_init(struct smb2 *chip) switch (pmic_rev_id->pmic_subtype) { case PMI8998_SUBTYPE: chip->chg.smb_version = PMI8998_SUBTYPE; - chip->chg.wa_flags |= BOOST_BACK_WA | QC_AUTH_INTERRUPT_WA_BIT; + chip->chg.wa_flags |= BOOST_BACK_WA | QC_AUTH_INTERRUPT_WA_BIT + | TYPEC_PBS_WA_BIT; if (pmic_rev_id->rev4 == PMI8998_V1P1_REV4) /* PMI rev 1.1 */ chg->wa_flags |= QC_CHARGER_DETECTION_WA_BIT; if (pmic_rev_id->rev4 == PMI8998_V2P0_REV4) /* PMI rev 2.0 */ @@ -1844,7 +1845,8 @@ static int smb2_chg_config_init(struct smb2 *chip) break; case PM660_SUBTYPE: chip->chg.smb_version = PM660_SUBTYPE; - chip->chg.wa_flags |= BOOST_BACK_WA | OTG_WA; + chip->chg.wa_flags |= BOOST_BACK_WA | OTG_WA | OV_IRQ_WA_BIT + | TYPEC_PBS_WA_BIT; chg->param.freq_buck = pm660_params.freq_buck; chg->param.freq_boost = pm660_params.freq_boost; chg->chg_freq.freq_5V = 650; diff --git a/drivers/power/supply/qcom/smb-lib.c b/drivers/power/supply/qcom/smb-lib.c index 6d3316b934de..0ed748b5d582 100644 --- a/drivers/power/supply/qcom/smb-lib.c +++ b/drivers/power/supply/qcom/smb-lib.c @@ -670,6 +670,7 @@ static void smblib_uusb_removal(struct smb_charger *chg) vote(chg->pl_enable_votable_indirect, USBIN_V_VOTER, false, 0); vote(chg->usb_icl_votable, SW_QC3_VOTER, false, 0); vote(chg->usb_icl_votable, USBIN_USBIN_BOOST_VOTER, false, 0); + vote(chg->hvdcp_hw_inov_dis_votable, OV_VOTER, false, 0); cancel_delayed_work_sync(&chg->hvdcp_detect_work); @@ -2012,6 +2013,18 @@ static int smblib_dm_pulse(struct smb_charger *chg) return rc; } +static int smblib_force_vbus_voltage(struct smb_charger *chg, u8 val) +{ + int rc; + + rc = smblib_masked_write(chg, CMD_HVDCP_2_REG, val, val); + if (rc < 0) + smblib_err(chg, "Couldn't write to CMD_HVDCP_2_REG rc=%d\n", + rc); + + return rc; +} + int smblib_dp_dm(struct smb_charger *chg, int val) { int target_icl_ua, rc = 0; @@ -2063,6 +2076,21 @@ int smblib_dp_dm(struct smb_charger *chg, int val) smblib_dbg(chg, PR_PARALLEL, "ICL DOWN ICL=%d reduction=%d\n", target_icl_ua, chg->usb_icl_delta_ua); break; + case POWER_SUPPLY_DP_DM_FORCE_5V: + rc = smblib_force_vbus_voltage(chg, FORCE_5V_BIT); + if (rc < 0) + pr_err("Failed to force 5V\n"); + break; + case POWER_SUPPLY_DP_DM_FORCE_9V: + rc = smblib_force_vbus_voltage(chg, FORCE_9V_BIT); + if (rc < 0) + pr_err("Failed to force 9V\n"); + break; + case POWER_SUPPLY_DP_DM_FORCE_12V: + rc = smblib_force_vbus_voltage(chg, FORCE_12V_BIT); + if (rc < 0) + pr_err("Failed to force 12V\n"); + break; case POWER_SUPPLY_DP_DM_ICL_UP: default: break; @@ -2634,19 +2662,21 @@ int smblib_set_prop_typec_power_role(struct smb_charger *chg, return -EINVAL; } - if (power_role == UFP_EN_CMD_BIT) { - /* disable PBS workaround when forcing sink mode */ - rc = smblib_write(chg, TM_IO_DTEST4_SEL, 0x0); - if (rc < 0) { - smblib_err(chg, "Couldn't write to TM_IO_DTEST4_SEL rc=%d\n", - rc); - } - } else { - /* restore it back to 0xA5 */ - rc = smblib_write(chg, TM_IO_DTEST4_SEL, 0xA5); - if (rc < 0) { - smblib_err(chg, "Couldn't write to TM_IO_DTEST4_SEL rc=%d\n", - rc); + if (chg->wa_flags & TYPEC_PBS_WA_BIT) { + if (power_role == UFP_EN_CMD_BIT) { + /* disable PBS workaround when forcing sink mode */ + rc = smblib_write(chg, TM_IO_DTEST4_SEL, 0x0); + if (rc < 0) { + smblib_err(chg, "Couldn't write to TM_IO_DTEST4_SEL rc=%d\n", + rc); + } + } else { + /* restore it back to 0xA5 */ + rc = smblib_write(chg, TM_IO_DTEST4_SEL, 0xA5); + if (rc < 0) { + smblib_err(chg, "Couldn't write to TM_IO_DTEST4_SEL rc=%d\n", + rc); + } } } @@ -3426,6 +3456,33 @@ static void smblib_handle_sdp_enumeration_done(struct smb_charger *chg, rising ? "rising" : "falling"); } +#define MICRO_10P3V 10300000 +static void smblib_check_ov_condition(struct smb_charger *chg) +{ + union power_supply_propval pval = {0, }; + int rc; + + if (chg->wa_flags & OV_IRQ_WA_BIT) { + rc = power_supply_get_property(chg->usb_psy, + POWER_SUPPLY_PROP_VOLTAGE_NOW, &pval); + if (rc < 0) { + smblib_err(chg, "Couldn't get current voltage, rc=%d\n", + rc); + return; + } + + if (pval.intval > MICRO_10P3V) { + smblib_err(chg, "USBIN OV detected\n"); + vote(chg->hvdcp_hw_inov_dis_votable, OV_VOTER, true, + 0); + pval.intval = POWER_SUPPLY_DP_DM_FORCE_5V; + rc = power_supply_set_property(chg->batt_psy, + POWER_SUPPLY_PROP_DP_DM, &pval); + return; + } + } +} + #define QC3_PULSES_FOR_6V 5 #define QC3_PULSES_FOR_9V 20 #define QC3_PULSES_FOR_12V 35 @@ -3435,6 +3492,7 @@ static void smblib_hvdcp_adaptive_voltage_change(struct smb_charger *chg) u8 stat; int pulses; + smblib_check_ov_condition(chg); power_supply_changed(chg->usb_main_psy); if (chg->real_charger_type == POWER_SUPPLY_TYPE_USB_HVDCP) { rc = smblib_read(chg, QC_CHANGE_STATUS_REG, &stat); @@ -3967,6 +4025,7 @@ static void smblib_handle_typec_removal(struct smb_charger *chg) /* reset hvdcp voters */ vote(chg->hvdcp_disable_votable_indirect, VBUS_CC_SHORT_VOTER, true, 0); vote(chg->hvdcp_disable_votable_indirect, PD_INACTIVE_VOTER, true, 0); + vote(chg->hvdcp_hw_inov_dis_votable, OV_VOTER, false, 0); /* reset power delivery voters */ vote(chg->pd_allowed_votable, PD_VOTER, false, 0); @@ -4041,10 +4100,13 @@ static void smblib_handle_typec_removal(struct smb_charger *chg) if (rc < 0) smblib_err(chg, "Couldn't enable HW cc_out rc=%d\n", rc); - /* restore crude sensor */ - rc = smblib_write(chg, TM_IO_DTEST4_SEL, 0xA5); - if (rc < 0) - smblib_err(chg, "Couldn't restore crude sensor rc=%d\n", rc); + /* restore crude sensor if PM660/PMI8998 */ + if (chg->wa_flags & TYPEC_PBS_WA_BIT) { + rc = smblib_write(chg, TM_IO_DTEST4_SEL, 0xA5); + if (rc < 0) + smblib_err(chg, "Couldn't restore crude sensor rc=%d\n", + rc); + } mutex_lock(&chg->vconn_oc_lock); if (!chg->vconn_en) diff --git a/drivers/power/supply/qcom/smb-lib.h b/drivers/power/supply/qcom/smb-lib.h index f292ca09f532..0de99b9da7bd 100644 --- a/drivers/power/supply/qcom/smb-lib.h +++ b/drivers/power/supply/qcom/smb-lib.h @@ -66,6 +66,7 @@ enum print_reason { #define USBIN_I_VOTER "USBIN_I_VOTER" #define WEAK_CHARGER_VOTER "WEAK_CHARGER_VOTER" #define WBC_VOTER "WBC_VOTER" +#define OV_VOTER "OV_VOTER" #define VCONN_MAX_ATTEMPTS 3 #define OTG_MAX_ATTEMPTS 3 @@ -84,6 +85,8 @@ enum { TYPEC_CC2_REMOVAL_WA_BIT = BIT(2), QC_AUTH_INTERRUPT_WA_BIT = BIT(3), OTG_WA = BIT(4), + OV_IRQ_WA_BIT = BIT(5), + TYPEC_PBS_WA_BIT = BIT(6), }; enum smb_irq_index { 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/glink_smem_native_xprt.c b/drivers/soc/qcom/glink_smem_native_xprt.c index df94bbc6b696..6cf4c7b6dd8a 100644 --- a/drivers/soc/qcom/glink_smem_native_xprt.c +++ b/drivers/soc/qcom/glink_smem_native_xprt.c @@ -171,8 +171,6 @@ struct mailbox_config_info { * @kwork: Work to be executed when an irq is received. * @kworker: Handle to the entity processing of deferred commands. - * @tasklet Handle to tasklet to process incoming data - packets in atomic manner. * @task: Handle to the task context used to run @kworker. * @use_ref: Active uses of this transport use this to grab * a reference. Used for ssr synchronization. @@ -216,7 +214,6 @@ struct edge_info { struct kthread_work kwork; struct kthread_worker kworker; struct task_struct *task; - struct tasklet_struct tasklet; struct srcu_struct use_ref; bool in_ssr; spinlock_t rx_lock; @@ -1191,18 +1188,6 @@ static void __rx_worker(struct edge_info *einfo, bool atomic_ctx) } /** - * rx_worker_atomic() - worker function to process received command in atomic - * context. - * @param: The param parameter passed during initialization of the tasklet. - */ -static void rx_worker_atomic(unsigned long param) -{ - struct edge_info *einfo = (struct edge_info *)param; - - __rx_worker(einfo, true); -} - -/** * rx_worker() - worker function to process received commands * @work: kwork associated with the edge to process commands on. */ @@ -1221,7 +1206,7 @@ irqreturn_t irq_handler(int irq, void *priv) if (einfo->rx_reset_reg) writel_relaxed(einfo->out_irq_mask, einfo->rx_reset_reg); - tasklet_hi_schedule(&einfo->tasklet); + __rx_worker(einfo, true); einfo->rx_irq_count++; return IRQ_HANDLED; @@ -2373,7 +2358,6 @@ static int glink_smem_native_probe(struct platform_device *pdev) init_waitqueue_head(&einfo->tx_blocked_queue); init_kthread_work(&einfo->kwork, rx_worker); init_kthread_worker(&einfo->kworker); - tasklet_init(&einfo->tasklet, rx_worker_atomic, (unsigned long)einfo); einfo->read_from_fifo = read_from_fifo; einfo->write_to_fifo = write_to_fifo; init_srcu_struct(&einfo->use_ref); @@ -2477,7 +2461,6 @@ smem_alloc_fail: flush_kthread_worker(&einfo->kworker); kthread_stop(einfo->task); einfo->task = NULL; - tasklet_kill(&einfo->tasklet); kthread_fail: iounmap(einfo->out_irq_reg); ioremap_fail: @@ -2563,7 +2546,6 @@ static int glink_rpm_native_probe(struct platform_device *pdev) init_waitqueue_head(&einfo->tx_blocked_queue); init_kthread_work(&einfo->kwork, rx_worker); init_kthread_worker(&einfo->kworker); - tasklet_init(&einfo->tasklet, rx_worker_atomic, (unsigned long)einfo); einfo->intentless = true; einfo->read_from_fifo = memcpy32_fromio; einfo->write_to_fifo = memcpy32_toio; @@ -2725,7 +2707,6 @@ toc_init_fail: flush_kthread_worker(&einfo->kworker); kthread_stop(einfo->task); einfo->task = NULL; - tasklet_kill(&einfo->tasklet); kthread_fail: iounmap(msgram); msgram_ioremap_fail: @@ -2854,7 +2835,6 @@ static int glink_mailbox_probe(struct platform_device *pdev) init_waitqueue_head(&einfo->tx_blocked_queue); init_kthread_work(&einfo->kwork, rx_worker); init_kthread_worker(&einfo->kworker); - tasklet_init(&einfo->tasklet, rx_worker_atomic, (unsigned long)einfo); einfo->read_from_fifo = read_from_fifo; einfo->write_to_fifo = write_to_fifo; init_srcu_struct(&einfo->use_ref); @@ -2977,7 +2957,6 @@ smem_alloc_fail: flush_kthread_worker(&einfo->kworker); kthread_stop(einfo->task); einfo->task = NULL; - tasklet_kill(&einfo->tasklet); kthread_fail: iounmap(einfo->rx_reset_reg); rx_reset_ioremap_fail: diff --git a/drivers/soc/qcom/glink_ssr.c b/drivers/soc/qcom/glink_ssr.c index fe7fb1e5b925..177737f3e314 100644 --- a/drivers/soc/qcom/glink_ssr.c +++ b/drivers/soc/qcom/glink_ssr.c @@ -538,7 +538,6 @@ int notify_for_subsystem(struct subsys_info *ss_info) * only modified during setup. */ atomic_set(&responses_remaining, ss_info->notify_list_len); - init_waitqueue_head(&waitqueue); notifications_successful = true; list_for_each_entry(ss_leaf_entry, &ss_info->notify_list, @@ -945,7 +944,7 @@ static int glink_ssr_probe(struct platform_device *pdev) ss_info->cb_data = NULL; spin_lock_init(&ss_info->link_up_lock); spin_lock_init(&ss_info->cb_lock); - + init_waitqueue_head(&waitqueue); nb = kmalloc(sizeof(struct restart_notifier_block), GFP_KERNEL); if (!nb) { GLINK_SSR_ERR("<SSR> %s: Could not allocate notifier block\n", 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/drivers/staging/android/ion/ion_page_pool.c b/drivers/staging/android/ion/ion_page_pool.c index c19b87d10df0..0034dfe17ac8 100644 --- a/drivers/staging/android/ion/ion_page_pool.c +++ b/drivers/staging/android/ion/ion_page_pool.c @@ -64,6 +64,9 @@ static int ion_page_pool_add(struct ion_page_pool *pool, struct page *page) list_add_tail(&page->lru, &pool->low_items); pool->low_count++; } + + mod_zone_page_state(page_zone(page), NR_INDIRECTLY_RECLAIMABLE_BYTES, + (1 << (PAGE_SHIFT + pool->order))); mutex_unlock(&pool->mutex); return 0; } @@ -83,6 +86,8 @@ static struct page *ion_page_pool_remove(struct ion_page_pool *pool, bool high) } list_del(&page->lru); + mod_zone_page_state(page_zone(page), NR_INDIRECTLY_RECLAIMABLE_BYTES, + -(1 << (PAGE_SHIFT + pool->order))); return page; } diff --git a/fs/proc/meminfo.c b/fs/proc/meminfo.c index 9155a5a0d3b9..b7594b9fa5fa 100644 --- a/fs/proc/meminfo.c +++ b/fs/proc/meminfo.c @@ -79,6 +79,13 @@ static int meminfo_proc_show(struct seq_file *m, void *v) available += global_page_state(NR_SLAB_RECLAIMABLE) - min(global_page_state(NR_SLAB_RECLAIMABLE) / 2, wmark_low); + /* + * Part of the kernel memory, which can be released under memory + * pressure. + */ + available += global_page_state(NR_INDIRECTLY_RECLAIMABLE_BYTES) >> + PAGE_SHIFT; + if (available < 0) available = 0; diff --git a/include/linux/diagchar.h b/include/linux/diagchar.h index 7c92113e20c3..112d8b73d066 100644 --- a/include/linux/diagchar.h +++ b/include/linux/diagchar.h @@ -1,4 +1,4 @@ -/* Copyright (c) 2008-2017, The Linux Foundation. All rights reserved. +/* Copyright (c) 2008-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 @@ -145,10 +145,10 @@ the appropriate macros. */ /* This needs to be modified manually now, when we add a new RANGE of SSIDs to the msg_mask_tbl */ #define MSG_MASK_TBL_CNT 26 -#define APPS_EVENT_LAST_ID 0x0B3F +#define APPS_EVENT_LAST_ID 0x0C5A #define MSG_SSID_0 0 -#define MSG_SSID_0_LAST 121 +#define MSG_SSID_0_LAST 125 #define MSG_SSID_1 500 #define MSG_SSID_1_LAST 506 #define MSG_SSID_2 1000 @@ -160,11 +160,11 @@ the appropriate macros. */ #define MSG_SSID_5 4000 #define MSG_SSID_5_LAST 4010 #define MSG_SSID_6 4500 -#define MSG_SSID_6_LAST 4583 +#define MSG_SSID_6_LAST 4584 #define MSG_SSID_7 4600 -#define MSG_SSID_7_LAST 4615 +#define MSG_SSID_7_LAST 4616 #define MSG_SSID_8 5000 -#define MSG_SSID_8_LAST 5033 +#define MSG_SSID_8_LAST 5034 #define MSG_SSID_9 5500 #define MSG_SSID_9_LAST 5516 #define MSG_SSID_10 6000 @@ -264,7 +264,7 @@ static const uint32_t msg_bld_masks_0[] = { MSG_MASK_6|MSG_MASK_7|MSG_MASK_8|MSG_MASK_9|MSG_MASK_10, MSG_LVL_MED, MSG_LVL_LOW, - MSG_LVL_LOW, + MSG_LVL_MED, MSG_LVL_MED, MSG_LVL_LOW, MSG_LVL_LOW, @@ -317,7 +317,7 @@ static const uint32_t msg_bld_masks_0[] = { MSG_LVL_LOW|MSG_LVL_MED|MSG_LVL_HIGH|MSG_LVL_ERROR|MSG_LVL_FATAL, MSG_LVL_MED, MSG_LVL_HIGH, - MSG_LVL_LOW, + MSG_LVL_MED, MSG_LVL_HIGH, MSG_LVL_LOW|MSG_LVL_MED|MSG_LVL_HIGH|MSG_LVL_ERROR|MSG_LVL_FATAL, MSG_LVL_MED|MSG_LVL_HIGH|MSG_LVL_ERROR, @@ -486,6 +486,7 @@ static const uint32_t msg_bld_masks_6[] = { MSG_LVL_LOW, MSG_LVL_LOW, MSG_LVL_LOW, + MSG_LVL_LOW, MSG_LVL_LOW }; @@ -505,7 +506,9 @@ static const uint32_t msg_bld_masks_7[] = { MSG_LVL_LOW, MSG_LVL_LOW, MSG_LVL_LOW, - MSG_LVL_LOW|MSG_LVL_MED|MSG_LVL_HIGH|MSG_LVL_ERROR|MSG_LVL_FATAL + MSG_LVL_LOW | MSG_LVL_MED | MSG_LVL_HIGH | MSG_LVL_ERROR | + MSG_LVL_FATAL, + MSG_LVL_LOW }; static const uint32_t msg_bld_masks_8[] = { @@ -525,9 +528,6 @@ static const uint32_t msg_bld_masks_8[] = { MSG_LVL_MED, MSG_LVL_MED, MSG_LVL_MED, - MSG_LVL_LOW, - MSG_LVL_LOW, - MSG_LVL_LOW, MSG_LVL_MED, MSG_LVL_MED, MSG_LVL_MED, @@ -542,6 +542,10 @@ static const uint32_t msg_bld_masks_8[] = { MSG_LVL_MED, MSG_LVL_MED, MSG_LVL_MED, + MSG_LVL_MED, + MSG_LVL_MED, + MSG_LVL_MED, + MSG_LVL_HIGH, MSG_LVL_HIGH }; @@ -644,14 +648,14 @@ static const uint32_t msg_bld_masks_10[] = { MSG_LVL_MED, MSG_LVL_MED, MSG_LVL_LOW, - MSG_LVL_LOW, - MSG_LVL_LOW, - MSG_LVL_LOW, - MSG_LVL_LOW, - MSG_LVL_LOW, - MSG_LVL_LOW, - MSG_LVL_LOW, - MSG_LVL_LOW, + MSG_LVL_MED, + MSG_LVL_MED, + MSG_LVL_MED, + MSG_LVL_MED, + MSG_LVL_MED, + MSG_LVL_MED, + MSG_LVL_MED, + MSG_LVL_MED, MSG_LVL_MED }; @@ -797,7 +801,9 @@ static const uint32_t msg_bld_masks_19[] = { }; static const uint32_t msg_bld_masks_20[] = { - MSG_LVL_LOW, + MSG_LVL_LOW | MSG_MASK_5 | MSG_MASK_6 | MSG_MASK_7 | + MSG_MASK_8 | MSG_MASK_9 | MSG_MASK_10 | MSG_MASK_11 | + MSG_MASK_12, MSG_LVL_LOW, MSG_LVL_LOW, MSG_LVL_LOW, @@ -875,7 +881,7 @@ static const uint32_t msg_bld_masks_25[] = { /* LOG CODES */ static const uint32_t log_code_last_tbl[] = { 0x0, /* EQUIP ID 0 */ - 0x1A11, /* EQUIP ID 1 */ + 0x1C68, /* EQUIP ID 1 */ 0x0, /* EQUIP ID 2 */ 0x0, /* EQUIP ID 3 */ 0x4910, /* EQUIP ID 4 */ @@ -885,7 +891,7 @@ static const uint32_t log_code_last_tbl[] = { 0x0, /* EQUIP ID 8 */ 0x0, /* EQUIP ID 9 */ 0xA38A, /* EQUIP ID 10 */ - 0xB201, /* EQUIP ID 11 */ + 0xB9FF, /* EQUIP ID 11 */ 0x0, /* EQUIP ID 12 */ 0xD1FF, /* EQUIP ID 13 */ 0x0, /* EQUIP ID 14 */ diff --git a/include/linux/mmzone.h b/include/linux/mmzone.h index 0db2f3cb1b6c..f09c5b28ed70 100644 --- a/include/linux/mmzone.h +++ b/include/linux/mmzone.h @@ -176,6 +176,7 @@ enum zone_stat_item { NR_ANON_TRANSPARENT_HUGEPAGES, NR_FREE_CMA_PAGES, NR_SWAPCACHE, + NR_INDIRECTLY_RECLAIMABLE_BYTES, /* measured in bytes */ NR_VM_ZONE_STAT_ITEMS }; /* diff --git a/include/linux/power_supply.h b/include/linux/power_supply.h index 8b8a46ce32d0..64d0797cc3a7 100644 --- a/include/linux/power_supply.h +++ b/include/linux/power_supply.h @@ -103,6 +103,9 @@ enum { POWER_SUPPLY_DP_DM_HVDCP3_SUPPORTED = 10, POWER_SUPPLY_DP_DM_ICL_DOWN = 11, POWER_SUPPLY_DP_DM_ICL_UP = 12, + POWER_SUPPLY_DP_DM_FORCE_5V = 13, + POWER_SUPPLY_DP_DM_FORCE_9V = 14, + POWER_SUPPLY_DP_DM_FORCE_12V = 15, }; enum { 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/sound/apr_audio-v2.h b/include/sound/apr_audio-v2.h index 0393c8869b8f..ee65bdae9971 100644 --- a/include/sound/apr_audio-v2.h +++ b/include/sound/apr_audio-v2.h @@ -3512,6 +3512,263 @@ struct afe_param_id_set_topology_cfg { u32 topology_id; } __packed; +/* + * This command is used by client to request the LPASS resources. + * Currently this command supports only LPAIF DMA resources. + * Allocated resources will be in control of remote client until + * they get released. + * + * If all the requested resources are available then response status in + * AFE_CMDRSP_REQUEST_LPASS_RESOURCES payload will + * be updated with ADSP_EOK, otherwise it will be ADSP_EFAILED. + * + * This command is variable payload size command, and size depends + * on the type of resource requested. + * + * For example, if client requests AFE_LPAIF_DMA_RESOURCE_ID + * resources, afe_cmd_request_lpass_resources structure will + * be followed with the afe_cmd_request_lpass_dma_resources + * structure. + * + * AFE_CMDRSP_REQUEST_LPASS_RESOURCES is the response for + * this command, which returns the allocated resources. + * + * @apr_hdr_fields + * Opcode -- AFE_CMD_REQUEST_LPASS_RESOURCES + * + * @return + * #AFE_CMDRSP_REQUEST_LPASS_RESOURCES + */ +#define AFE_CMD_REQUEST_LPASS_RESOURCES 0x00010109 + +/* Macro for requesting LPAIF DMA resources */ +#define AFE_LPAIF_DMA_RESOURCE_ID 0x00000001 + +struct afe_cmd_request_lpass_resources { + /* + * LPASS Resource ID + * @values: + * - AFE_LPAIF_DMA_RESOURCE_ID + */ + u32 resource_id; +} __packed; + +/* + * AFE_CMD_REQUEST_LPASS_RESOURCES uses this structure when + * client is requesting LPAIF DMA resources. + * + * Number of read DMA channels and write DMA channels varies from chipset to + * chipset. HLOS needs to make sure that when it requests LPASS DMA + * resources, it should not impact the concurrencies which + * are mandatory for a given chipset. + */ + +/* Macro for AFE LPAIF default DMA data type */ +#define AFE_LPAIF_DEFAULT_DMA_TYPE 0x0 + +struct afe_cmd_request_lpass_dma_resources { + /* + * LPASS DMA Type + * @values: + * - AFE_LPAIF_DEFAULT_DMA_TYPE + */ + u8 dma_type; + /* + * Number of read DMA channels required + * @values: >=0 + * - 0 indicates channels are not requested + */ + u8 num_read_dma_channels; + /* + * Number of write DMA channels required + * @values: >=0 + * - 0 indicates channels are not requested + */ + u8 num_write_dma_channels; + /* + * Reserved field for 4 byte alignment + * @values: 0 + */ + u8 reserved; +} __packed; + +struct afe_request_lpass_dma_resources_command { + struct apr_hdr hdr; + struct afe_cmd_request_lpass_resources resources; + struct afe_cmd_request_lpass_dma_resources dma_resources; +} __packed; + +/* + * This is the response for the command AFE_CMD_REQUEST_LPASS_RESOURCES. + * Payload of this command is variable. + * + * Resources allocated successfully or not, are determined by the "status" + * in the payload. If status is ADSP_EOK, then resources are + * allocated successfully and allocated resource information + * follows. + * + * For example, if the response resource id is AFE_LPAIF_DMA_RESOURCE_ID, + * afe_cmdrsp_request_lpass_dma_resources structure will + * follow after afe_cmdrsp_request_lpass_resources. + * + * If status is ADSP_EFAILED, this indicates requested resources + * are not allocated successfully. In this case the payload following + * this structure is invalid. + * @apr_hdr_fields + * Opcode -- AFE_CMDRSP_REQUEST_LPASS_RESOURCES +*/ +#define AFE_CMDRSP_REQUEST_LPASS_RESOURCES 0x0001010A + +struct afe_cmdrsp_request_lpass_resources { + /* + * ADSP_EOK if all requested resources are allocated. + * ADSP_EFAILED if resource allocation is failed. + */ + u32 status; + /* + * Returned LPASS DMA resource ID + * @values: + * - AFE_LPAIF_DMA_RESOURCE_ID + */ + u32 resource_id; +} __packed; + +/* + * This command will be sent as a payload for + * AFE_CMDRSP_REQUEST_LPASS_RESOURCES, when the LPAIF DMA resources + * were requested. Payload of this command is variable, which + * follows after the afe_cmdrsp_request_lpass_dma_resources structure. + * The size in bytes following this structure is sum of + * num_read_dma_channels and num_write_dma_channels. + * + * If the resource allocation is successful, then the payload contains + * the valid DMA channel indices. + * + * For example, if number of requested DMA read channels is 2, and they + * are successfully allocated, the variable payload contains + * valid DMA channel index values in first two bytes array. + * + * In the failure case this payload can be ignored, and all the values will be + * initialized with zeros. + * + * An example payload of the command response is below: + * <struct afe_cmdrsp_request_lpass_resources> + * <struct afe_cmdrsp_request_lpass_dma_resources> + * read DMA index value for each byte. + * write DMA index value for each byte. + * padded zeros, if sum of num_read_dma_channels and num_write_dma_channels + * are not multiples of 4. +*/ + +struct afe_cmdrsp_request_lpass_dma_resources { + /* + * LPASS DMA Type + * @values: + * - AFE_LPAIF_DEFAULT_DMA_TYPE + */ + u8 dma_type; + /* + * Returned number of read DMA channels allocated + * @values: >=0 + */ + u8 num_read_dma_channels; + /* + * Returned number of write DMA channels allocated + * @values: >=0 + */ + u8 num_write_dma_channels; + /* + * Reserved field for 4 byte alignment + * @values: 0 + */ + u8 reserved; +} __packed; + +/* + * This command is for releasing resources which are allocated as + * part of AFE_CMD_REQUEST_LPASS_RESOURCES. + * + * Payload of this command is variable, which follows + * after the afe_cmd_release_lpass_resources structure. + * + * If release resource is AFE_LPAIF_DMA_RESOURCE_ID + * afe_cmd_release_lpass_dma_resources structure will be + * followed after afe_cmd_release_lpass_resources. + * + * + * @apr_hdr_fields + * Opcode -- AFE_CMD_RELEASE_LPASS_RESOURCES + + * @return + * #APRv2 IBASIC RSP Result +*/ +#define AFE_CMD_RELEASE_LPASS_RESOURCES 0x0001010B + +struct afe_cmd_release_lpass_resources { + /* + * LPASS DMA resource ID + * @values: + * - AFE_LPAIF_DMA_RESOURCE_ID + */ + u32 resource_id; +} __packed; + +/* + * This payload to be appended as part of AFE_CMD_RELEASE_LPASS_RESOURCES + * when resource id AFE_LPAIF_DMA_RESOURCE_ID is used. + * + * Payload of this command is variable, which will be followed after the + * afe_cmd_release_lpass_dma_resources structure. + * The variable payload's size in bytes is sum of + * num_read_dma_channels and num_write_dma_channels. + * Variable payload data contains the valid DMA channel indices which are + * allocated as part of AFE_CMD_REQUEST_LPASS_RESOURCES. + * + * For example, if number of DMA read channels released are 2, + * the variable payload contains valid DMA channel + * index values in first two bytes of variable payload. + * Client needs to fill the same DMA channel indices were returned + * as part of AFE_CMD_RELEASE_LPASS_RESOURCES, otherwise + * ADSP will return the error. + * + * An example payload of the release command is below: + * <struct afe_cmd_release_lpass_resources> + * <struct afe_cmd_release_lpass_dma_resources> + * read DMA index value for each byte. + * write DMA index value for each byte. +*/ + +struct afe_cmd_release_lpass_dma_resources { + /* + * LPASS DMA Type + * @values: + * - AFE_LPAIF_DEFAULT_DMA_TYPE + */ + u8 dma_type; + /* + * Number of read DMA channels to be released + * @values: >=0 + * - 0 indicates channels are not released + */ + u8 num_read_dma_channels; + /* + * Number of write DMA channels to be released + * @values: >=0 + * - 0 indicates channels are not released + */ + u8 num_write_dma_channels; + /* + * Reserved field for 4 byte alignment + * @values: 0 + */ + u8 reserved; +} __packed; + +struct afe_release_lpass_dma_resources_command { + struct apr_hdr hdr; + struct afe_cmd_release_lpass_resources resources; + struct afe_cmd_release_lpass_dma_resources dma_resources; +} __packed; /* * Generic encoder module ID. diff --git a/include/sound/q6afe-v2.h b/include/sound/q6afe-v2.h index 922ebb69205d..cdbf97023f66 100644 --- a/include/sound/q6afe-v2.h +++ b/include/sound/q6afe-v2.h @@ -1,4 +1,4 @@ -/* Copyright (c) 2012-2017, The Linux Foundation. All rights reserved. +/* Copyright (c) 2012-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 @@ -42,6 +42,9 @@ #define AFE_CLK_VERSION_V1 1 #define AFE_CLK_VERSION_V2 2 +#define AFE_MAX_RDDMA 10 +#define AFE_MAX_WRDMA 10 + typedef int (*routing_cb)(int port); enum { @@ -450,4 +453,9 @@ void afe_set_routing_callback(routing_cb); int afe_get_av_dev_drift(struct afe_param_id_dev_timing_stats *timing_stats, u16 port); int afe_get_svc_version(uint32_t service_id); +int afe_request_dma_resources(uint8_t dma_type, uint8_t num_read_dma_channels, + uint8_t num_write_dma_channels); +int afe_get_dma_idx(bool **ret_rddma_idx, + bool **ret_wrdma_idx); +int afe_release_all_dma_resources(void); #endif /* __Q6AFE_V2_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 */ diff --git a/kernel/taskstats.c b/kernel/taskstats.c index 21f82c29c914..11cc757795cd 100644 --- a/kernel/taskstats.c +++ b/kernel/taskstats.c @@ -54,7 +54,11 @@ static const struct nla_policy taskstats_cmd_get_policy[TASKSTATS_CMD_ATTR_MAX+1 [TASKSTATS_CMD_ATTR_REGISTER_CPUMASK] = { .type = NLA_STRING }, [TASKSTATS_CMD_ATTR_DEREGISTER_CPUMASK] = { .type = NLA_STRING },}; -static const struct nla_policy cgroupstats_cmd_get_policy[CGROUPSTATS_CMD_ATTR_MAX+1] = { +/* + * We have to use TASKSTATS_CMD_ATTR_MAX here, it is the maxattr in the family. + * Make sure they are always aligned. + */ +static const struct nla_policy cgroupstats_cmd_get_policy[TASKSTATS_CMD_ATTR_MAX+1] = { [CGROUPSTATS_CMD_ATTR_FD] = { .type = NLA_U32 }, }; diff --git a/mm/mmap.c b/mm/mmap.c index 2339b533f4b2..5457c5f4935b 100644 --- a/mm/mmap.c +++ b/mm/mmap.c @@ -206,6 +206,13 @@ int __vm_enough_memory(struct mm_struct *mm, long pages, int cap_sys_admin) free += global_page_state(NR_SLAB_RECLAIMABLE); /* + * Part of the kernel memory, which can be released + * under memory pressure. + */ + free += global_page_state( + NR_INDIRECTLY_RECLAIMABLE_BYTES) >> PAGE_SHIFT; + + /* * Leave reserved pages. The pages are not for anonymous pages. */ if (free <= totalreserve_pages) diff --git a/mm/nommu.c b/mm/nommu.c index 92be862c859b..8d75e425c21c 100644 --- a/mm/nommu.c +++ b/mm/nommu.c @@ -1880,6 +1880,13 @@ int __vm_enough_memory(struct mm_struct *mm, long pages, int cap_sys_admin) free += global_page_state(NR_SLAB_RECLAIMABLE); /* + * Part of the kernel memory, which can be released + * under memory pressure. + */ + free += global_page_state( + NR_INDIRECTLY_RECLAIMABLE_BYTES) >> PAGE_SHIFT; + + /* * Leave reserved pages. The pages are not for anonymous pages. */ if (free <= totalreserve_pages) diff --git a/mm/vmstat.c b/mm/vmstat.c index 6c841595b963..9ab13e3be5df 100644 --- a/mm/vmstat.c +++ b/mm/vmstat.c @@ -765,6 +765,7 @@ const char * const vmstat_text[] = { "nr_anon_transparent_hugepages", "nr_free_cma", "nr_swapcache", + "nr_indirectly_reclaimable", /* enum writeback_stat_item counters */ "nr_dirty_threshold", diff --git a/net/netfilter/ipvs/ip_vs_ctl.c b/net/netfilter/ipvs/ip_vs_ctl.c index 2f0e4f61c40f..9979f4a1053b 100644 --- a/net/netfilter/ipvs/ip_vs_ctl.c +++ b/net/netfilter/ipvs/ip_vs_ctl.c @@ -2808,7 +2808,7 @@ static struct genl_family ip_vs_genl_family = { .hdrsize = 0, .name = IPVS_GENL_NAME, .version = IPVS_GENL_VERSION, - .maxattr = IPVS_CMD_MAX, + .maxattr = IPVS_CMD_ATTR_MAX, .netnsok = true, /* Make ipvsadm to work on netns */ }; diff --git a/sound/soc/codecs/sdm660_cdc/msm-analog-cdc.c b/sound/soc/codecs/sdm660_cdc/msm-analog-cdc.c index 55eef61a01de..fe455c9b8c25 100644 --- a/sound/soc/codecs/sdm660_cdc/msm-analog-cdc.c +++ b/sound/soc/codecs/sdm660_cdc/msm-analog-cdc.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2015-2017, The Linux Foundation. All rights reserved. +/* Copyright (c) 2015-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 @@ -3805,12 +3805,11 @@ static int msm_anlg_cdc_device_down(struct snd_soc_codec *codec) } msm_anlg_cdc_boost_off(codec); sdm660_cdc_priv->hph_mode = NORMAL_MODE; - - /* 40ms to allow boost to discharge */ - msleep(40); /* Disable PA to avoid pop during codec bring up */ snd_soc_update_bits(codec, MSM89XX_PMIC_ANALOG_RX_HPH_CNP_EN, 0x30, 0x00); + /* 40ms to allow boost to discharge */ + msleep(40); snd_soc_update_bits(codec, MSM89XX_PMIC_ANALOG_SPKR_DRV_CTL, 0x80, 0x00); snd_soc_write(codec, diff --git a/sound/soc/msm/apq8096-auto.c b/sound/soc/msm/apq8096-auto.c index 0de8b0237aae..a0c5ef0dce6d 100644 --- a/sound/soc/msm/apq8096-auto.c +++ b/sound/soc/msm/apq8096-auto.c @@ -5699,6 +5699,22 @@ static struct snd_soc_dai_link apq8096_auto_fe_dai_links[] = { .ignore_pmdown_time = 1, .codec_dai_name = "snd-soc-dummy-dai", .codec_name = "snd-soc-dummy", + }, + { + .name = "DTMF RX Hostless", + .stream_name = "DTMF RX Hostless", + .cpu_dai_name = "DTMF_RX_HOSTLESS", + .platform_name = "msm-pcm-dtmf", + .dynamic = 1, + .dpcm_playback = 1, + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST}, + .no_host_mode = SND_SOC_DAI_LINK_NO_HOST, + .ignore_suspend = 1, + .ignore_pmdown_time = 1, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + .be_id = MSM_FRONTEND_DAI_DTMF_RX, } }; diff --git a/sound/soc/msm/qdsp6v2/msm-pcm-routing-v2.c b/sound/soc/msm/qdsp6v2/msm-pcm-routing-v2.c index 8a28c4fa6746..4e4970b7be33 100644 --- a/sound/soc/msm/qdsp6v2/msm-pcm-routing-v2.c +++ b/sound/soc/msm/qdsp6v2/msm-pcm-routing-v2.c @@ -8916,6 +8916,9 @@ static const struct snd_kcontrol_new quat_tdm_rx_2_voice_mixer_controls[] = { SOC_SINGLE_EXT("VoiceMMode1", MSM_BACKEND_DAI_QUAT_TDM_RX_2, MSM_FRONTEND_DAI_VOICEMMODE1, 1, 0, msm_routing_get_voice_mixer, msm_routing_put_voice_mixer), + SOC_SINGLE_EXT("DTMF", MSM_BACKEND_DAI_QUAT_TDM_RX_2, + MSM_FRONTEND_DAI_DTMF_RX, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_mixer), }; static const struct snd_kcontrol_new stub_rx_mixer_controls[] = { @@ -14954,6 +14957,7 @@ static const struct snd_soc_dapm_route intercon[] = { {"QUIN_MI2S_RX", NULL, "QUIN_MI2S_RX_Voice Mixer"}, {"QUAT_TDM_RX_2_Voice Mixer", "VoiceMMode1", "VOICEMMODE1_DL"}, + {"QUAT_TDM_RX_2_Voice Mixer", "DTMF", "DTMF_DL_HL"}, {"QUAT_TDM_RX_2", NULL, "QUAT_TDM_RX_2_Voice Mixer"}, {"VOC_EXT_EC MUX", "PRI_MI2S_TX" , "PRI_MI2S_TX"}, diff --git a/sound/soc/msm/qdsp6v2/q6afe.c b/sound/soc/msm/qdsp6v2/q6afe.c index 84ab632d9b9c..38dc3639a682 100644 --- a/sound/soc/msm/qdsp6v2/q6afe.c +++ b/sound/soc/msm/qdsp6v2/q6afe.c @@ -124,6 +124,10 @@ struct afe_ctl { int set_custom_topology; int dev_acdb_id[AFE_MAX_PORTS]; routing_cb rt_cb; + int num_alloced_rddma; + bool alloced_rddma[AFE_MAX_RDDMA]; + int num_alloced_wrdma; + bool alloced_wrdma[AFE_MAX_WRDMA]; }; static atomic_t afe_ports_mad_type[SLIMBUS_PORT_LAST - SLIMBUS_0_RX]; @@ -385,6 +389,99 @@ static int32_t sp_make_afe_callback(uint32_t opcode, uint32_t *payload, return 0; } +static int32_t afe_lpass_resources_callback(struct apr_client_data *data) +{ + uint8_t *payload = data->payload; + struct afe_cmdrsp_request_lpass_resources *resources = + (struct afe_cmdrsp_request_lpass_resources *) payload; + struct afe_cmdrsp_request_lpass_dma_resources *dma_resources = NULL; + uint8_t *dma_channels_id_payload = NULL; + + 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); + atomic_set(&this_afe.status, ADSP_EBADPARAM); + return -EINVAL; + } + + if (resources->status != 0) { + pr_debug("%s: Error: Requesting LPASS resources ret %d\n", + __func__, resources->status); + atomic_set(&this_afe.status, ADSP_EBADPARAM); + return -EINVAL; + } + + if (resources->resource_id == AFE_LPAIF_DMA_RESOURCE_ID) { + int i; + + payload += sizeof( + struct afe_cmdrsp_request_lpass_resources); + dma_resources = (struct + afe_cmdrsp_request_lpass_dma_resources *) + payload; + + pr_debug("%s: DMA Type allocated = %d\n", + __func__, + dma_resources->dma_type); + + if (dma_resources->num_read_dma_channels > AFE_MAX_RDDMA) { + pr_err("%s: Allocated Read DMA %d exceeds max %d\n", + __func__, + dma_resources->num_read_dma_channels, + AFE_MAX_RDDMA); + dma_resources->num_read_dma_channels = AFE_MAX_RDDMA; + } + + if (dma_resources->num_write_dma_channels > AFE_MAX_WRDMA) { + pr_err("%s: Allocated Write DMA %d exceeds max %d\n", + __func__, + dma_resources->num_write_dma_channels, + AFE_MAX_WRDMA); + dma_resources->num_write_dma_channels = AFE_MAX_WRDMA; + } + + this_afe.num_alloced_rddma = + dma_resources->num_read_dma_channels; + this_afe.num_alloced_wrdma = + dma_resources->num_write_dma_channels; + + pr_debug("%s: Number of allocated Read DMA channels= %d\n", + __func__, + dma_resources->num_read_dma_channels); + pr_debug("%s: Number of allocated Write DMA channels= %d\n", + __func__, + dma_resources->num_write_dma_channels); + + payload += sizeof( + struct afe_cmdrsp_request_lpass_dma_resources); + dma_channels_id_payload = payload; + + for (i = 0; i < this_afe.num_alloced_rddma; i++) { + pr_debug("%s: Read DMA Index %d allocated\n", + __func__, *dma_channels_id_payload); + this_afe.alloced_rddma + [*dma_channels_id_payload] = 1; + dma_channels_id_payload++; + } + + for (i = 0; i < this_afe.num_alloced_wrdma; i++) { + pr_debug("%s: Write DMA Index %d allocated\n", + __func__, *dma_channels_id_payload); + this_afe.alloced_wrdma + [*dma_channels_id_payload] = 1; + dma_channels_id_payload++; + } + } else { + pr_err("%s: Error: Unknown resource ID %d", + __func__, resources->resource_id); + atomic_set(&this_afe.status, ADSP_EBADPARAM); + return -EINVAL; + } + + return 0; +} + static int32_t afe_callback(struct apr_client_data *data, void *priv) { if (!data) { @@ -472,6 +569,15 @@ static int32_t afe_callback(struct apr_client_data *data, void *priv) return -EINVAL; } wake_up(&this_afe.wait[data->token]); + } else if (data->opcode == AFE_CMDRSP_REQUEST_LPASS_RESOURCES) { + uint32_t ret = 0; + + ret = afe_lpass_resources_callback(data); + atomic_set(&this_afe.state, 0); + wake_up(&this_afe.wait[data->token]); + if (!ret) { + return ret; + } } else if (data->payload_size) { uint32_t *payload; uint16_t port_id = 0; @@ -502,6 +608,7 @@ static int32_t afe_callback(struct apr_client_data *data, void *priv) case AFE_PORTS_CMD_DTMF_CTL: case AFE_SVC_CMD_SET_PARAM: case AFE_SVC_CMD_SET_PARAM_V2: + case AFE_CMD_REQUEST_LPASS_RESOURCES: atomic_set(&this_afe.state, 0); wake_up(&this_afe.wait[data->token]); break; @@ -541,6 +648,18 @@ static int32_t afe_callback(struct apr_client_data *data, void *priv) atomic_set(&this_afe.state, payload[1]); wake_up(&this_afe.wait[data->token]); break; + case AFE_CMD_RELEASE_LPASS_RESOURCES: + memset(&this_afe.alloced_rddma[0], + 0, + AFE_MAX_RDDMA); + memset(&this_afe.alloced_wrdma[0], + 0, + AFE_MAX_WRDMA); + this_afe.num_alloced_rddma = 0; + this_afe.num_alloced_wrdma = 0; + atomic_set(&this_afe.state, 0); + wake_up(&this_afe.wait[data->token]); + break; default: pr_err("%s: Unknown cmd 0x%x\n", __func__, payload[0]); @@ -6598,6 +6717,173 @@ done: return result; } +int afe_request_dma_resources(uint8_t dma_type, uint8_t num_read_dma_channels, + uint8_t num_write_dma_channels) +{ + int result = 0; + struct afe_request_lpass_dma_resources_command config; + + pr_debug("%s:\n", __func__); + + if (dma_type != AFE_LPAIF_DEFAULT_DMA_TYPE) { + pr_err("%s: DMA type %d is invalid\n", + __func__, + dma_type); + goto done; + } + + if ((num_read_dma_channels == 0) && + (num_write_dma_channels == 0)) { + pr_err("%s: DMA channels to allocate are 0\n", + __func__); + goto done; + } + + if (num_read_dma_channels > AFE_MAX_RDDMA) { + pr_err("%s: Read DMA channels %d to allocate are > %d\n", + __func__, + num_read_dma_channels, + AFE_MAX_RDDMA); + goto done; + } + + if (num_write_dma_channels > AFE_MAX_WRDMA) { + pr_err("%s: Write DMA channels %d to allocate are > %d\n", + __func__, + num_write_dma_channels, + AFE_MAX_WRDMA); + goto done; + } + + result = afe_q6_interface_prepare(); + if (result != 0) { + pr_err("%s: Q6 interface prepare failed %d\n", + __func__, result); + goto done; + } + + 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 = IDX_GLOBAL_CFG; + config.hdr.opcode = AFE_CMD_REQUEST_LPASS_RESOURCES; + config.resources.resource_id = AFE_LPAIF_DMA_RESOURCE_ID; + /* Only AFE_LPAIF_DEFAULT_DMA_TYPE dma type is supported */ + config.dma_resources.dma_type = dma_type; + config.dma_resources.num_read_dma_channels = num_read_dma_channels; + config.dma_resources.num_write_dma_channels = num_write_dma_channels; + + result = afe_apr_send_pkt(&config, &this_afe.wait[IDX_GLOBAL_CFG]); + if (result) + pr_err("%s: AFE_CMD_REQUEST_LPASS_RESOURCES failed %d\n", + __func__, result); + +done: + return result; +} +EXPORT_SYMBOL(afe_request_dma_resources); + +int afe_get_dma_idx(bool **ret_rddma_idx, + bool **ret_wrdma_idx) +{ + int ret = 0; + + if (!ret_rddma_idx || !ret_wrdma_idx) { + pr_err("%s: invalid return pointers.", __func__); + ret = -EINVAL; + goto done; + } + + *ret_rddma_idx = &this_afe.alloced_rddma[0]; + *ret_wrdma_idx = &this_afe.alloced_wrdma[0]; + +done: + return ret; +} +EXPORT_SYMBOL(afe_get_dma_idx); + +int afe_release_all_dma_resources(void) +{ + int result = 0; + int i, total_size; + struct afe_release_lpass_dma_resources_command *config; + uint8_t *payload; + + pr_debug("%s:\n", __func__); + + if ((this_afe.num_alloced_rddma == 0) && + (this_afe.num_alloced_wrdma == 0)) { + pr_err("%s: DMA channels to release is 0", + __func__); + goto done; + } + + result = afe_q6_interface_prepare(); + if (result != 0) { + pr_err("%s: Q6 interface prepare failed %d\n", + __func__, result); + goto done; + } + + total_size = sizeof(struct afe_release_lpass_dma_resources_command) + + sizeof(uint8_t) * + (this_afe.num_alloced_rddma + this_afe.num_alloced_wrdma); + + config = kzalloc(total_size, GFP_KERNEL); + if (!config) { + result = -ENOMEM; + goto done; + } + + memset(config, 0, total_size); + payload = (uint8_t *) config + + sizeof(struct afe_release_lpass_dma_resources_command); + + 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 = total_size; + config->hdr.src_port = 0; + config->hdr.dest_port = 0; + config->hdr.token = IDX_GLOBAL_CFG; + config->hdr.opcode = AFE_CMD_RELEASE_LPASS_RESOURCES; + config->resources.resource_id = AFE_LPAIF_DMA_RESOURCE_ID; + /* Only AFE_LPAIF_DEFAULT_DMA_TYPE dma type is supported */ + config->dma_resources.dma_type = AFE_LPAIF_DEFAULT_DMA_TYPE; + config->dma_resources.num_read_dma_channels = + this_afe.num_alloced_rddma; + config->dma_resources.num_write_dma_channels = + this_afe.num_alloced_wrdma; + + for (i = 0; i < AFE_MAX_RDDMA; i++) { + if (this_afe.alloced_rddma[i]) { + *payload = i; + payload++; + } + } + + for (i = 0; i < AFE_MAX_WRDMA; i++) { + if (this_afe.alloced_wrdma[i]) { + *payload = i; + payload++; + } + } + + result = afe_apr_send_pkt(config, &this_afe.wait[IDX_GLOBAL_CFG]); + if (result) + pr_err("%s: AFE_CMD_RELEASE_LPASS_RESOURCES failed %d\n", + __func__, result); + + kfree(config); +done: + return result; +} +EXPORT_SYMBOL(afe_release_all_dma_resources); + static int __init afe_init(void) { int i = 0, ret; diff --git a/sound/soc/msm/qdsp6v2/q6audio-v2.c b/sound/soc/msm/qdsp6v2/q6audio-v2.c index 3b745c24f90e..0062e4cd6432 100644 --- a/sound/soc/msm/qdsp6v2/q6audio-v2.c +++ b/sound/soc/msm/qdsp6v2/q6audio-v2.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2012-2017, The Linux Foundation. All rights reserved. +/* Copyright (c) 2012-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 @@ -794,6 +794,7 @@ int q6audio_validate_port(u16 port_id) case AFE_PORT_ID_INT5_MI2S_TX: case AFE_PORT_ID_INT6_MI2S_RX: case AFE_PORT_ID_INT6_MI2S_TX: + case AFE_PORT_ID_MULTICHAN_HDMI_RX: { ret = 0; break; |
